Coverage Report

Created: 2025-09-27 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wgpu/naga/src/front/wgsl/lower/conversion.rs
Line
Count
Source
1
//! WGSL's automatic conversions for abstract types.
2
3
use alloc::{boxed::Box, string::String, vec::Vec};
4
5
use crate::common::wgsl::{TryToWgsl, TypeContext};
6
use crate::front::wgsl::error::{
7
    AutoConversionError, AutoConversionLeafScalarError, ConcretizationFailedError,
8
};
9
use crate::front::wgsl::Result;
10
use crate::{Handle, Span};
11
12
impl<'source> super::ExpressionContext<'source, '_, '_> {
13
    /// Try to use WGSL's automatic conversions to convert `expr` to `goal_ty`.
14
    ///
15
    /// If no conversions are necessary, return `expr` unchanged.
16
    ///
17
    /// If automatic conversions cannot convert `expr` to `goal_ty`, return an
18
    /// [`AutoConversion`] error.
19
    ///
20
    /// Although the Load Rule is one of the automatic conversions, this
21
    /// function assumes it has already been applied if appropriate, as
22
    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.
23
    ///
24
    /// [`AutoConversion`]: super::Error::AutoConversion
25
0
    pub fn try_automatic_conversions(
26
0
        &mut self,
27
0
        expr: Handle<crate::Expression>,
28
0
        goal_ty: &crate::proc::TypeResolution,
29
0
        goal_span: Span,
30
0
    ) -> Result<'source, Handle<crate::Expression>> {
31
0
        let expr_span = self.get_expression_span(expr);
32
        // Keep the TypeResolution so we can get type names for
33
        // structs in error messages.
34
0
        let expr_resolution = super::resolve!(self, expr);
35
0
        let types = &self.module.types;
36
0
        let expr_inner = expr_resolution.inner_with(types);
37
0
        let goal_inner = goal_ty.inner_with(types);
38
39
        // We can only convert abstract types, so if `expr` is not abstract do not even
40
        // attempt conversion. This allows the validator to catch type errors correctly
41
        // rather than them being misreported as type conversion errors.
42
        // If the type is an array (of an array, etc) then we must check whether the
43
        // type of the innermost array's base type is abstract.
44
0
        if !expr_inner.is_abstract(types) {
45
0
            return Ok(expr);
46
0
        }
47
48
        // If `expr` already has the requested type, we're done.
49
0
        if self.module.compare_types(expr_resolution, goal_ty) {
50
0
            return Ok(expr);
51
0
        }
52
53
0
        let (_expr_scalar, goal_scalar) =
54
0
            match expr_inner.automatically_converts_to(goal_inner, types) {
55
0
                Some(scalars) => scalars,
56
                None => {
57
0
                    let source_type = self.type_resolution_to_string(expr_resolution);
58
0
                    let dest_type = self.type_resolution_to_string(goal_ty);
59
60
0
                    return Err(Box::new(super::Error::AutoConversion(Box::new(
61
0
                        AutoConversionError {
62
0
                            dest_span: goal_span,
63
0
                            dest_type,
64
0
                            source_span: expr_span,
65
0
                            source_type,
66
0
                        },
67
0
                    ))));
68
                }
69
            };
70
71
0
        self.convert_leaf_scalar(expr, expr_span, goal_scalar)
72
0
    }
73
74
    /// Try to convert `expr`'s leaf scalar to `goal_scalar` using automatic conversions.
75
    ///
76
    /// If no conversions are necessary, return `expr` unchanged.
77
    ///
78
    /// If automatic conversions cannot convert `expr` to `goal_scalar`, return
79
    /// an [`AutoConversionLeafScalar`] error.
80
    ///
81
    /// Although the Load Rule is one of the automatic conversions, this
82
    /// function assumes it has already been applied if appropriate, as
83
    /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`.
84
    ///
85
    /// [`AutoConversionLeafScalar`]: super::Error::AutoConversionLeafScalar
86
51
    pub fn try_automatic_conversion_for_leaf_scalar(
87
51
        &mut self,
88
51
        expr: Handle<crate::Expression>,
89
51
        goal_scalar: crate::Scalar,
90
51
        goal_span: Span,
91
51
    ) -> Result<'source, Handle<crate::Expression>> {
92
51
        let expr_span = self.get_expression_span(expr);
93
51
        let expr_resolution = super::resolve!(self, expr);
94
51
        let types = &self.module.types;
95
51
        let expr_inner = expr_resolution.inner_with(types);
96
97
51
        let make_error = || {
98
0
            let source_type = self.type_resolution_to_string(expr_resolution);
99
0
            super::Error::AutoConversionLeafScalar(Box::new(AutoConversionLeafScalarError {
100
0
                dest_span: goal_span,
101
0
                dest_scalar: goal_scalar.to_wgsl_for_diagnostics(),
102
0
                source_span: expr_span,
103
0
                source_type,
104
0
            }))
105
0
        };
106
107
51
        let expr_scalar = match expr_inner.automatically_convertible_scalar(&self.module.types) {
108
51
            Some(scalar) => scalar,
109
0
            None => return Err(Box::new(make_error())),
110
        };
111
112
51
        if expr_scalar == goal_scalar {
113
1
            return Ok(expr);
114
50
        }
115
116
50
        if !expr_scalar.automatically_converts_to(goal_scalar) {
117
0
            return Err(Box::new(make_error()));
118
50
        }
119
120
50
        assert!(expr_scalar.is_abstract());
121
122
50
        self.convert_leaf_scalar(expr, expr_span, goal_scalar)
123
51
    }
124
125
50
    fn convert_leaf_scalar(
126
50
        &mut self,
127
50
        expr: Handle<crate::Expression>,
128
50
        expr_span: Span,
129
50
        goal_scalar: crate::Scalar,
130
50
    ) -> Result<'source, Handle<crate::Expression>> {
131
50
        let expr_inner = super::resolve_inner!(self, expr);
132
50
        if let crate::TypeInner::Array { .. } = *expr_inner {
133
0
            self.as_const_evaluator()
134
0
                .cast_array(expr, goal_scalar, expr_span)
135
0
                .map_err(|err| {
136
0
                    Box::new(super::Error::ConstantEvaluatorError(err.into(), expr_span))
137
0
                })
138
        } else {
139
50
            let cast = crate::Expression::As {
140
50
                expr,
141
50
                kind: goal_scalar.kind,
142
50
                convert: Some(goal_scalar.width),
143
50
            };
144
50
            self.append_expression(cast, expr_span)
145
        }
146
50
    }
147
148
    /// Try to convert `exprs` to `goal_ty` using WGSL's automatic conversions.
149
0
    pub fn try_automatic_conversions_slice(
150
0
        &mut self,
151
0
        exprs: &mut [Handle<crate::Expression>],
152
0
        goal_ty: &crate::proc::TypeResolution,
153
0
        goal_span: Span,
154
0
    ) -> Result<'source, ()> {
155
0
        for expr in exprs.iter_mut() {
156
0
            *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
157
        }
158
159
0
        Ok(())
160
0
    }
161
162
    /// Apply WGSL's automatic conversions to a vector constructor's arguments.
163
    ///
164
    /// When calling a vector constructor like `vec3<f32>(...)`, the parameters
165
    /// can be a mix of scalars and vectors, with the latter being spread out to
166
    /// contribute each of their components as a component of the new value.
167
    /// When the element type is explicit, as with `<f32>` in the example above,
168
    /// WGSL's automatic conversions should convert abstract scalar and vector
169
    /// parameters to the constructor's required scalar type.
170
0
    pub fn try_automatic_conversions_for_vector(
171
0
        &mut self,
172
0
        exprs: &mut [Handle<crate::Expression>],
173
0
        goal_scalar: crate::Scalar,
174
0
        goal_span: Span,
175
0
    ) -> Result<'source, ()> {
176
        use crate::proc::TypeResolution as Tr;
177
        use crate::TypeInner as Ti;
178
0
        let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
179
180
0
        for (i, expr) in exprs.iter_mut().enumerate() {
181
            // Keep the TypeResolution so we can get full type names
182
            // in error messages.
183
0
            let expr_resolution = super::resolve!(self, *expr);
184
0
            let types = &self.module.types;
185
0
            let expr_inner = expr_resolution.inner_with(types);
186
187
0
            match *expr_inner {
188
                Ti::Scalar(_) => {
189
0
                    *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
190
                }
191
0
                Ti::Vector { size, scalar: _ } => {
192
0
                    let goal_vector_res = Tr::Value(Ti::Vector {
193
0
                        size,
194
0
                        scalar: goal_scalar,
195
0
                    });
196
0
                    *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
197
                }
198
                _ => {
199
0
                    let span = self.get_expression_span(*expr);
200
0
                    return Err(Box::new(super::Error::InvalidConstructorComponentType(
201
0
                        span, i as i32,
202
0
                    )));
203
                }
204
            }
205
        }
206
207
0
        Ok(())
208
0
    }
209
210
    /// Convert `expr` to the leaf scalar type `scalar`.
211
1.56k
    pub fn convert_to_leaf_scalar(
212
1.56k
        &mut self,
213
1.56k
        expr: &mut Handle<crate::Expression>,
214
1.56k
        goal: crate::Scalar,
215
1.56k
    ) -> Result<'source, ()> {
216
1.56k
        let inner = super::resolve_inner!(self, *expr);
217
        // Do nothing if `inner` doesn't even have leaf scalars;
218
        // it's a type error that validation will catch.
219
1.56k
        if inner.scalar() != Some(goal) {
220
3
            let cast = crate::Expression::As {
221
3
                expr: *expr,
222
3
                kind: goal.kind,
223
3
                convert: Some(goal.width),
224
3
            };
225
3
            let expr_span = self.get_expression_span(*expr);
226
3
            *expr = self.append_expression(cast, expr_span)?;
227
1.56k
        }
228
229
1.56k
        Ok(())
230
1.56k
    }
231
232
    /// Convert all expressions in `exprs` to a common scalar type.
233
    ///
234
    /// Note that the caller is responsible for making sure these
235
    /// conversions are actually justified. This function simply
236
    /// generates `As` expressions, regardless of whether they are
237
    /// permitted WGSL automatic conversions. Callers intending to
238
    /// implement automatic conversions need to determine for
239
    /// themselves whether the casts we we generate are justified,
240
    /// perhaps by calling `TypeInner::automatically_converts_to` or
241
    /// `Scalar::automatic_conversion_combine`.
242
2
    pub fn convert_slice_to_common_leaf_scalar(
243
2
        &mut self,
244
2
        exprs: &mut [Handle<crate::Expression>],
245
2
        goal: crate::Scalar,
246
2
    ) -> Result<'source, ()> {
247
4
        for expr in exprs.iter_mut() {
248
4
            self.convert_to_leaf_scalar(expr, goal)?;
249
        }
250
251
2
        Ok(())
252
2
    }
253
254
    /// Return an expression for the concretized value of `expr`.
255
    ///
256
    /// If `expr` is already concrete, return it unchanged.
257
14.8k
    pub fn concretize(
258
14.8k
        &mut self,
259
14.8k
        mut expr: Handle<crate::Expression>,
260
14.8k
    ) -> Result<'source, Handle<crate::Expression>> {
261
14.8k
        let inner = super::resolve_inner!(self, expr);
262
14.8k
        if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
263
14.8k
            let concretized = scalar.concretize();
264
14.8k
            if concretized != scalar {
265
7
                assert!(scalar.is_abstract());
266
7
                let expr_span = self.get_expression_span(expr);
267
7
                expr = self
268
7
                    .as_const_evaluator()
269
7
                    .cast_array(expr, concretized, expr_span)
270
7
                    .map_err(|err| {
271
                        // A `TypeResolution` includes the type's full name, if
272
                        // it has one. Also, avoid holding the borrow of `inner`
273
                        // across the call to `cast_array`.
274
0
                        let expr_type = &self.typifier()[expr];
275
0
                        super::Error::ConcretizationFailed(Box::new(ConcretizationFailedError {
276
0
                            expr_span,
277
0
                            expr_type: self.type_resolution_to_string(expr_type),
278
0
                            scalar: concretized.to_wgsl_for_diagnostics(),
279
0
                            inner: err,
280
0
                        }))
281
0
                    })?;
282
14.8k
            }
283
0
        }
284
285
14.8k
        Ok(expr)
286
14.8k
    }
287
288
    /// Find the consensus scalar of `components` under WGSL's automatic
289
    /// conversions.
290
    ///
291
    /// If `components` can all be converted to any common scalar via
292
    /// WGSL's automatic conversions, return the best such scalar.
293
    ///
294
    /// The `components` slice must not be empty. All elements' types must
295
    /// have been resolved.
296
    ///
297
    /// If `components` are definitely not acceptable as arguments to such
298
    /// constructors, return `Err(i)`, where `i` is the index in
299
    /// `components` of some problematic argument.
300
    ///
301
    /// This function doesn't fully type-check the arguments - it only
302
    /// considers their leaf scalar types. This means it may return `Ok`
303
    /// even when the Naga validator will reject the resulting
304
    /// construction expression later.
305
782
    pub fn automatic_conversion_consensus<'handle, I>(
306
782
        &self,
307
782
        components: I,
308
782
    ) -> core::result::Result<crate::Scalar, usize>
309
782
    where
310
782
        I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
311
782
        I::IntoIter: Clone, // for debugging
312
    {
313
782
        let types = &self.module.types;
314
782
        let components_iter = components.into_iter();
315
782
        log::debug!(
316
0
            "wgsl automatic_conversion_consensus: {}",
317
0
            components_iter
318
0
                .clone()
319
0
                .map(|&expr| {
320
0
                    let res = &self.typifier()[expr];
321
0
                    self.type_resolution_to_string(res)
322
0
                })
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#1}
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]>::{closure#1}
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#1}
323
0
                .collect::<Vec<String>>()
324
0
                .join(", ")
325
        );
326
1.56k
        let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#0}
Line
Count
Source
326
1.56k
        let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]>::{closure#0}
Line
Count
Source
326
4
        let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#0}
327
782
        let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
328
782
        for (inner, i) in inners.zip(1..) {
329
782
            let scalar = inner.scalar().ok_or(i)?;
330
782
            match best.automatic_conversion_combine(scalar) {
331
782
                Some(new_best) => {
332
782
                    best = new_best;
333
782
                }
334
0
                None => return Err(i),
335
            }
336
        }
337
338
782
        log::debug!("    consensus: {}", best.to_wgsl_for_diagnostics());
339
782
        Ok(best)
340
782
    }
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>>
Line
Count
Source
305
780
    pub fn automatic_conversion_consensus<'handle, I>(
306
780
        &self,
307
780
        components: I,
308
780
    ) -> core::result::Result<crate::Scalar, usize>
309
780
    where
310
780
        I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
311
780
        I::IntoIter: Clone, // for debugging
312
    {
313
780
        let types = &self.module.types;
314
780
        let components_iter = components.into_iter();
315
780
        log::debug!(
316
0
            "wgsl automatic_conversion_consensus: {}",
317
0
            components_iter
318
0
                .clone()
319
0
                .map(|&expr| {
320
                    let res = &self.typifier()[expr];
321
                    self.type_resolution_to_string(res)
322
                })
323
0
                .collect::<Vec<String>>()
324
0
                .join(", ")
325
        );
326
780
        let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
327
780
        let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
328
780
        for (inner, i) in inners.zip(1..) {
329
780
            let scalar = inner.scalar().ok_or(i)?;
330
780
            match best.automatic_conversion_combine(scalar) {
331
780
                Some(new_best) => {
332
780
                    best = new_best;
333
780
                }
334
0
                None => return Err(i),
335
            }
336
        }
337
338
780
        log::debug!("    consensus: {}", best.to_wgsl_for_diagnostics());
339
780
        Ok(best)
340
780
    }
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]>
Line
Count
Source
305
2
    pub fn automatic_conversion_consensus<'handle, I>(
306
2
        &self,
307
2
        components: I,
308
2
    ) -> core::result::Result<crate::Scalar, usize>
309
2
    where
310
2
        I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
311
2
        I::IntoIter: Clone, // for debugging
312
    {
313
2
        let types = &self.module.types;
314
2
        let components_iter = components.into_iter();
315
2
        log::debug!(
316
0
            "wgsl automatic_conversion_consensus: {}",
317
0
            components_iter
318
0
                .clone()
319
0
                .map(|&expr| {
320
                    let res = &self.typifier()[expr];
321
                    self.type_resolution_to_string(res)
322
                })
323
0
                .collect::<Vec<String>>()
324
0
                .join(", ")
325
        );
326
2
        let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
327
2
        let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
328
2
        for (inner, i) in inners.zip(1..) {
329
2
            let scalar = inner.scalar().ok_or(i)?;
330
2
            match best.automatic_conversion_combine(scalar) {
331
2
                Some(new_best) => {
332
2
                    best = new_best;
333
2
                }
334
0
                None => return Err(i),
335
            }
336
        }
337
338
2
        log::debug!("    consensus: {}", best.to_wgsl_for_diagnostics());
339
2
        Ok(best)
340
2
    }
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>>
341
}
342
343
impl crate::TypeInner {
344
14.9k
    fn automatically_convertible_scalar(
345
14.9k
        &self,
346
14.9k
        types: &crate::UniqueArena<crate::Type>,
347
14.9k
    ) -> Option<crate::Scalar> {
348
        use crate::TypeInner as Ti;
349
14.9k
        match *self {
350
9.41k
            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
351
14.9k
                Some(scalar)
352
            }
353
0
            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
0
            | Ti::BindingArray { .. } => None,
363
        }
364
14.9k
    }
365
366
    /// Return the leaf scalar type of `pointer`.
367
    ///
368
    /// `pointer` must be a `TypeInner` representing a pointer type.
369
50
    pub fn pointer_automatically_convertible_scalar(
370
50
        &self,
371
50
        types: &crate::UniqueArena<crate::Type>,
372
50
    ) -> Option<crate::Scalar> {
373
        use crate::TypeInner as Ti;
374
50
        match *self {
375
0
            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
376
0
                Some(scalar)
377
            }
378
0
            Ti::Atomic(_) => None,
379
50
            Ti::Pointer { base, .. } | Ti::Array { base, .. } => {
380
50
                types[base].inner.automatically_convertible_scalar(types)
381
            }
382
0
            Ti::ValuePointer { scalar, .. } => Some(scalar),
383
            Ti::Struct { .. }
384
            | Ti::Image { .. }
385
            | Ti::Sampler { .. }
386
            | Ti::AccelerationStructure { .. }
387
            | Ti::RayQuery { .. }
388
0
            | Ti::BindingArray { .. } => None,
389
        }
390
50
    }
391
}
392
393
impl crate::Scalar {
394
    /// Find the common type of `self` and `other` under WGSL's
395
    /// automatic conversions.
396
    ///
397
    /// If there are any scalars to which WGSL's automatic conversions
398
    /// will convert both `self` and `other`, return the best such
399
    /// scalar. Otherwise, return `None`.
400
832
    pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
401
        use crate::ScalarKind as Sk;
402
403
832
        match (self.kind, other.kind) {
404
            // When the kinds match...
405
            (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
779
                if self.width == other.width {
412
                    // ... either no conversion is necessary ...
413
779
                    Some(self)
414
                } else {
415
                    // ... or no conversion is possible.
416
                    // We never convert concrete to concrete, and
417
                    // abstract types should have only one size.
418
0
                    None
419
                }
420
            }
421
422
            // AbstractInt converts to AbstractFloat.
423
2
            (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
424
0
            (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
425
426
            // AbstractFloat converts to Float.
427
0
            (Sk::AbstractFloat, Sk::Float) => Some(other),
428
0
            (Sk::Float, Sk::AbstractFloat) => Some(self),
429
430
            // AbstractInt converts to concrete integer or float.
431
51
            (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
432
0
            (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
433
434
            // AbstractFloat can't be reconciled with concrete integer types.
435
            (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
436
0
                None
437
            }
438
439
            // Nothing can be reconciled with `bool`.
440
0
            (Sk::Bool, _) | (_, Sk::Bool) => None,
441
442
            // Different concrete types cannot be reconciled.
443
0
            (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
444
        }
445
832
    }
446
447
    /// Return `true` if automatic conversions will covert `self` to `goal`.
448
50
    pub fn automatically_converts_to(self, goal: Self) -> bool {
449
50
        self.automatic_conversion_combine(goal) == Some(goal)
450
50
    }
451
452
14.8k
    pub(in crate::front::wgsl) const fn concretize(self) -> Self {
453
        use crate::ScalarKind as Sk;
454
14.8k
        match self.kind {
455
14.8k
            Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
456
5
            Sk::AbstractInt => Self::I32,
457
2
            Sk::AbstractFloat => Self::F32,
458
        }
459
14.8k
    }
460
}