naga/front/glsl/
functions.rs

1use alloc::{
2    format,
3    string::{String, ToString},
4    vec,
5    vec::Vec,
6};
7use core::iter;
8
9use super::{
10    ast::*,
11    builtins::{inject_builtin, sampled_to_depth},
12    context::{Context, ExprPos, StmtContext},
13    error::{Error, ErrorKind},
14    types::scalar_components,
15    Frontend, Result,
16};
17use crate::{
18    front::glsl::types::type_power, proc::ensure_block_returns, AddressSpace, Block, EntryPoint,
19    Expression, Function, FunctionArgument, FunctionResult, Handle, Literal, LocalVariable, Scalar,
20    ScalarKind, Span, Statement, StructMember, Type, TypeInner,
21};
22
23/// Struct detailing a store operation that must happen after a function call
24struct ProxyWrite {
25    /// The store target
26    target: Handle<Expression>,
27    /// A pointer to read the value of the store
28    value: Handle<Expression>,
29    /// An optional conversion to be applied
30    convert: Option<Scalar>,
31}
32
33impl Frontend {
34    pub(crate) fn function_or_constructor_call(
35        &mut self,
36        ctx: &mut Context,
37        stmt: &StmtContext,
38        fc: FunctionCallKind,
39        raw_args: &[Handle<HirExpr>],
40        meta: Span,
41    ) -> Result<Option<Handle<Expression>>> {
42        let args: Vec<_> = raw_args
43            .iter()
44            .map(|e| ctx.lower_expect_inner(stmt, self, *e, ExprPos::Rhs))
45            .collect::<Result<_>>()?;
46
47        match fc {
48            FunctionCallKind::TypeConstructor(ty) => {
49                if args.len() == 1 {
50                    self.constructor_single(ctx, ty, args[0], meta).map(Some)
51                } else {
52                    self.constructor_many(ctx, ty, args, meta).map(Some)
53                }
54            }
55            FunctionCallKind::Function(name) => {
56                self.function_call(ctx, stmt, name, args, raw_args, meta)
57            }
58        }
59    }
60
61    fn constructor_single(
62        &mut self,
63        ctx: &mut Context,
64        ty: Handle<Type>,
65        (mut value, expr_meta): (Handle<Expression>, Span),
66        meta: Span,
67    ) -> Result<Handle<Expression>> {
68        let expr_type = ctx.resolve_type(value, expr_meta)?;
69
70        let vector_size = match *expr_type {
71            TypeInner::Vector { size, .. } => Some(size),
72            _ => None,
73        };
74
75        let expr_is_bool = expr_type.scalar_kind() == Some(ScalarKind::Bool);
76
77        // Special case: if casting from a bool, we need to use Select and not As.
78        match ctx.module.types[ty].inner.scalar() {
79            Some(result_scalar) if expr_is_bool && result_scalar.kind != ScalarKind::Bool => {
80                let result_scalar = Scalar {
81                    width: 4,
82                    ..result_scalar
83                };
84                let l0 = Literal::zero(result_scalar).unwrap();
85                let l1 = Literal::one(result_scalar).unwrap();
86                let mut reject = ctx.add_expression(Expression::Literal(l0), expr_meta)?;
87                let mut accept = ctx.add_expression(Expression::Literal(l1), expr_meta)?;
88
89                ctx.implicit_splat(&mut reject, meta, vector_size)?;
90                ctx.implicit_splat(&mut accept, meta, vector_size)?;
91
92                let h = ctx.add_expression(
93                    Expression::Select {
94                        accept,
95                        reject,
96                        condition: value,
97                    },
98                    expr_meta,
99                )?;
100
101                return Ok(h);
102            }
103            _ => {}
104        }
105
106        Ok(match ctx.module.types[ty].inner {
107            TypeInner::Vector { size, scalar } if vector_size.is_none() => {
108                ctx.forced_conversion(&mut value, expr_meta, scalar)?;
109
110                if let TypeInner::Scalar { .. } = *ctx.resolve_type(value, expr_meta)? {
111                    ctx.add_expression(Expression::Splat { size, value }, meta)?
112                } else {
113                    self.vector_constructor(ctx, ty, size, scalar, &[(value, expr_meta)], meta)?
114                }
115            }
116            TypeInner::Scalar(scalar) => {
117                let mut expr = value;
118                if let TypeInner::Vector { .. } | TypeInner::Matrix { .. } =
119                    *ctx.resolve_type(value, expr_meta)?
120                {
121                    expr = ctx.add_expression(
122                        Expression::AccessIndex {
123                            base: expr,
124                            index: 0,
125                        },
126                        meta,
127                    )?;
128                }
129
130                if let TypeInner::Matrix { .. } = *ctx.resolve_type(value, expr_meta)? {
131                    expr = ctx.add_expression(
132                        Expression::AccessIndex {
133                            base: expr,
134                            index: 0,
135                        },
136                        meta,
137                    )?;
138                }
139
140                ctx.add_expression(
141                    Expression::As {
142                        kind: scalar.kind,
143                        expr,
144                        convert: Some(scalar.width),
145                    },
146                    meta,
147                )?
148            }
149            TypeInner::Vector { size, scalar } => {
150                if vector_size != Some(size) {
151                    value = ctx.vector_resize(size, value, expr_meta)?;
152                }
153
154                ctx.add_expression(
155                    Expression::As {
156                        kind: scalar.kind,
157                        expr: value,
158                        convert: Some(scalar.width),
159                    },
160                    meta,
161                )?
162            }
163            TypeInner::Matrix {
164                columns,
165                rows,
166                scalar,
167            } => self.matrix_one_arg(ctx, ty, columns, rows, scalar, (value, expr_meta), meta)?,
168            TypeInner::Struct { ref members, .. } => {
169                let scalar_components = members
170                    .first()
171                    .and_then(|member| scalar_components(&ctx.module.types[member.ty].inner));
172                if let Some(scalar) = scalar_components {
173                    ctx.implicit_conversion(&mut value, expr_meta, scalar)?;
174                }
175
176                ctx.add_expression(
177                    Expression::Compose {
178                        ty,
179                        components: vec![value],
180                    },
181                    meta,
182                )?
183            }
184
185            TypeInner::Array { base, .. } => {
186                let scalar_components = scalar_components(&ctx.module.types[base].inner);
187                if let Some(scalar) = scalar_components {
188                    ctx.implicit_conversion(&mut value, expr_meta, scalar)?;
189                }
190
191                ctx.add_expression(
192                    Expression::Compose {
193                        ty,
194                        components: vec![value],
195                    },
196                    meta,
197                )?
198            }
199            _ => {
200                self.errors.push(Error {
201                    kind: ErrorKind::SemanticError("Bad type constructor".into()),
202                    meta,
203                });
204
205                value
206            }
207        })
208    }
209
210    #[allow(clippy::too_many_arguments)]
211    fn matrix_one_arg(
212        &mut self,
213        ctx: &mut Context,
214        ty: Handle<Type>,
215        columns: crate::VectorSize,
216        rows: crate::VectorSize,
217        element_scalar: Scalar,
218        (mut value, expr_meta): (Handle<Expression>, Span),
219        meta: Span,
220    ) -> Result<Handle<Expression>> {
221        let mut components = Vec::with_capacity(columns as usize);
222        // TODO: casts
223        // `Expression::As` doesn't support matrix width
224        // casts so we need to do some extra work for casts
225
226        ctx.forced_conversion(&mut value, expr_meta, element_scalar)?;
227        match *ctx.resolve_type(value, expr_meta)? {
228            TypeInner::Scalar(_) => {
229                // If a matrix is constructed with a single scalar value, then that
230                // value is used to initialize all the values along the diagonal of
231                // the matrix; the rest are given zeros.
232                let vector_ty = ctx.module.types.insert(
233                    Type {
234                        name: None,
235                        inner: TypeInner::Vector {
236                            size: rows,
237                            scalar: element_scalar,
238                        },
239                    },
240                    meta,
241                );
242
243                let zero_literal = Literal::zero(element_scalar).unwrap();
244                let zero = ctx.add_expression(Expression::Literal(zero_literal), meta)?;
245
246                for i in 0..columns as u32 {
247                    components.push(
248                        ctx.add_expression(
249                            Expression::Compose {
250                                ty: vector_ty,
251                                components: (0..rows as u32)
252                                    .map(|r| match r == i {
253                                        true => value,
254                                        false => zero,
255                                    })
256                                    .collect(),
257                            },
258                            meta,
259                        )?,
260                    )
261                }
262            }
263            TypeInner::Matrix {
264                rows: ori_rows,
265                columns: ori_cols,
266                ..
267            } => {
268                // If a matrix is constructed from a matrix, then each component
269                // (column i, row j) in the result that has a corresponding component
270                // (column i, row j) in the argument will be initialized from there. All
271                // other components will be initialized to the identity matrix.
272
273                let zero_literal = Literal::zero(element_scalar).unwrap();
274                let one_literal = Literal::one(element_scalar).unwrap();
275
276                let zero = ctx.add_expression(Expression::Literal(zero_literal), meta)?;
277                let one = ctx.add_expression(Expression::Literal(one_literal), meta)?;
278
279                let vector_ty = ctx.module.types.insert(
280                    Type {
281                        name: None,
282                        inner: TypeInner::Vector {
283                            size: rows,
284                            scalar: element_scalar,
285                        },
286                    },
287                    meta,
288                );
289
290                for i in 0..columns as u32 {
291                    if i < ori_cols as u32 {
292                        use core::cmp::Ordering;
293
294                        let vector = ctx.add_expression(
295                            Expression::AccessIndex {
296                                base: value,
297                                index: i,
298                            },
299                            meta,
300                        )?;
301
302                        components.push(match ori_rows.cmp(&rows) {
303                            Ordering::Less => {
304                                let components = (0..rows as u32)
305                                    .map(|r| {
306                                        if r < ori_rows as u32 {
307                                            ctx.add_expression(
308                                                Expression::AccessIndex {
309                                                    base: vector,
310                                                    index: r,
311                                                },
312                                                meta,
313                                            )
314                                        } else if r == i {
315                                            Ok(one)
316                                        } else {
317                                            Ok(zero)
318                                        }
319                                    })
320                                    .collect::<Result<_>>()?;
321
322                                ctx.add_expression(
323                                    Expression::Compose {
324                                        ty: vector_ty,
325                                        components,
326                                    },
327                                    meta,
328                                )?
329                            }
330                            Ordering::Equal => vector,
331                            Ordering::Greater => ctx.vector_resize(rows, vector, meta)?,
332                        })
333                    } else {
334                        let compose_expr = Expression::Compose {
335                            ty: vector_ty,
336                            components: (0..rows as u32)
337                                .map(|r| match r == i {
338                                    true => one,
339                                    false => zero,
340                                })
341                                .collect(),
342                        };
343
344                        let vec = ctx.add_expression(compose_expr, meta)?;
345
346                        components.push(vec)
347                    }
348                }
349            }
350            _ => {
351                components = iter::repeat_n(value, columns as usize).collect();
352            }
353        }
354
355        ctx.add_expression(Expression::Compose { ty, components }, meta)
356    }
357
358    #[allow(clippy::too_many_arguments)]
359    fn vector_constructor(
360        &mut self,
361        ctx: &mut Context,
362        ty: Handle<Type>,
363        size: crate::VectorSize,
364        scalar: Scalar,
365        args: &[(Handle<Expression>, Span)],
366        meta: Span,
367    ) -> Result<Handle<Expression>> {
368        let mut components = Vec::with_capacity(size as usize);
369
370        for (mut arg, expr_meta) in args.iter().copied() {
371            ctx.forced_conversion(&mut arg, expr_meta, scalar)?;
372
373            if components.len() >= size as usize {
374                break;
375            }
376
377            match *ctx.resolve_type(arg, expr_meta)? {
378                TypeInner::Scalar { .. } => components.push(arg),
379                TypeInner::Matrix { rows, columns, .. } => {
380                    components.reserve(rows as usize * columns as usize);
381                    for c in 0..(columns as u32) {
382                        let base = ctx.add_expression(
383                            Expression::AccessIndex {
384                                base: arg,
385                                index: c,
386                            },
387                            expr_meta,
388                        )?;
389                        for r in 0..(rows as u32) {
390                            components.push(ctx.add_expression(
391                                Expression::AccessIndex { base, index: r },
392                                expr_meta,
393                            )?)
394                        }
395                    }
396                }
397                TypeInner::Vector { size: ori_size, .. } => {
398                    components.reserve(ori_size as usize);
399                    for index in 0..(ori_size as u32) {
400                        components.push(ctx.add_expression(
401                            Expression::AccessIndex { base: arg, index },
402                            expr_meta,
403                        )?)
404                    }
405                }
406                _ => components.push(arg),
407            }
408        }
409
410        components.truncate(size as usize);
411
412        ctx.add_expression(Expression::Compose { ty, components }, meta)
413    }
414
415    fn constructor_many(
416        &mut self,
417        ctx: &mut Context,
418        ty: Handle<Type>,
419        args: Vec<(Handle<Expression>, Span)>,
420        meta: Span,
421    ) -> Result<Handle<Expression>> {
422        let mut components = Vec::with_capacity(args.len());
423
424        let struct_member_data = match ctx.module.types[ty].inner {
425            TypeInner::Matrix {
426                columns,
427                rows,
428                scalar: element_scalar,
429            } => {
430                let mut flattened = Vec::with_capacity(columns as usize * rows as usize);
431
432                for (mut arg, meta) in args.iter().copied() {
433                    ctx.forced_conversion(&mut arg, meta, element_scalar)?;
434
435                    match *ctx.resolve_type(arg, meta)? {
436                        TypeInner::Vector { size, .. } => {
437                            for i in 0..(size as u32) {
438                                flattened.push(ctx.add_expression(
439                                    Expression::AccessIndex {
440                                        base: arg,
441                                        index: i,
442                                    },
443                                    meta,
444                                )?)
445                            }
446                        }
447                        _ => flattened.push(arg),
448                    }
449                }
450
451                let ty = ctx.module.types.insert(
452                    Type {
453                        name: None,
454                        inner: TypeInner::Vector {
455                            size: rows,
456                            scalar: element_scalar,
457                        },
458                    },
459                    meta,
460                );
461
462                for chunk in flattened.chunks(rows as usize) {
463                    components.push(ctx.add_expression(
464                        Expression::Compose {
465                            ty,
466                            components: Vec::from(chunk),
467                        },
468                        meta,
469                    )?)
470                }
471                None
472            }
473            TypeInner::Vector { size, scalar } => {
474                return self.vector_constructor(ctx, ty, size, scalar, &args, meta)
475            }
476            TypeInner::Array { base, .. } => {
477                for (mut arg, meta) in args.iter().copied() {
478                    let scalar_components = scalar_components(&ctx.module.types[base].inner);
479                    if let Some(scalar) = scalar_components {
480                        ctx.implicit_conversion(&mut arg, meta, scalar)?;
481                    }
482
483                    components.push(arg)
484                }
485                None
486            }
487            TypeInner::Struct { ref members, .. } => Some(
488                members
489                    .iter()
490                    .map(|member| scalar_components(&ctx.module.types[member.ty].inner))
491                    .collect::<Vec<_>>(),
492            ),
493            _ => {
494                return Err(Error {
495                    kind: ErrorKind::SemanticError("Constructor: Too many arguments".into()),
496                    meta,
497                })
498            }
499        };
500
501        if let Some(struct_member_data) = struct_member_data {
502            for ((mut arg, meta), scalar_components) in
503                args.iter().copied().zip(struct_member_data.iter().copied())
504            {
505                if let Some(scalar) = scalar_components {
506                    ctx.implicit_conversion(&mut arg, meta, scalar)?;
507                }
508
509                components.push(arg)
510            }
511        }
512
513        ctx.add_expression(Expression::Compose { ty, components }, meta)
514    }
515
516    #[allow(clippy::too_many_arguments)]
517    fn function_call(
518        &mut self,
519        ctx: &mut Context,
520        stmt: &StmtContext,
521        name: String,
522        args: Vec<(Handle<Expression>, Span)>,
523        raw_args: &[Handle<HirExpr>],
524        meta: Span,
525    ) -> Result<Option<Handle<Expression>>> {
526        // Grow the typifier to be able to index it later without needing
527        // to hold the context mutably
528        for &(expr, span) in args.iter() {
529            ctx.typifier_grow(expr, span)?;
530        }
531
532        // Check if the passed arguments require any special variations
533        let mut variations =
534            builtin_required_variations(args.iter().map(|&(expr, _)| ctx.get_type(expr)));
535
536        // Initiate the declaration if it wasn't previously initialized and inject builtins
537        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {
538            variations |= BuiltinVariations::STANDARD;
539            Default::default()
540        });
541        inject_builtin(declaration, ctx.module, &name, variations);
542
543        // Borrow again but without mutability, at this point a declaration is guaranteed
544        let declaration = self.lookup_function.get(&name).unwrap();
545
546        // Possibly contains the overload to be used in the call
547        let mut maybe_overload = None;
548        // The conversions needed for the best analyzed overload, this is initialized all to
549        // `NONE` to make sure that conversions always pass the first time without ambiguity
550        let mut old_conversions = vec![Conversion::None; args.len()];
551        // Tracks whether the comparison between overloads lead to an ambiguity
552        let mut ambiguous = false;
553
554        // Iterate over all the available overloads to select either an exact match or a
555        // overload which has suitable implicit conversions
556        'outer: for (overload_idx, overload) in declaration.overloads.iter().enumerate() {
557            // If the overload and the function call don't have the same number of arguments
558            // continue to the next overload
559            if args.len() != overload.parameters.len() {
560                continue;
561            }
562
563            log::trace!("Testing overload {overload_idx}");
564
565            // Stores whether the current overload matches exactly the function call
566            let mut exact = true;
567            // State of the selection
568            // If None we still don't know what is the best overload
569            // If Some(true) the new overload is better
570            // If Some(false) the old overload is better
571            let mut superior = None;
572            // Store the conversions for the current overload so that later they can replace the
573            // conversions used for querying the best overload
574            let mut new_conversions = vec![Conversion::None; args.len()];
575
576            // Loop through the overload parameters and check if the current overload is better
577            // compared to the previous best overload.
578            for (i, overload_parameter) in overload.parameters.iter().enumerate() {
579                let call_argument = &args[i];
580                let parameter_info = &overload.parameters_info[i];
581
582                // If the image is used in the overload as a depth texture convert it
583                // before comparing, otherwise exact matches wouldn't be reported
584                if parameter_info.depth {
585                    sampled_to_depth(ctx, call_argument.0, call_argument.1, &mut self.errors);
586                    ctx.invalidate_expression(call_argument.0, call_argument.1)?
587                }
588
589                ctx.typifier_grow(call_argument.0, call_argument.1)?;
590
591                let overload_param_ty = &ctx.module.types[*overload_parameter].inner;
592                let call_arg_ty = ctx.get_type(call_argument.0);
593
594                log::trace!(
595                    "Testing parameter {i}\n\tOverload = {overload_param_ty:?}\n\tCall = {call_arg_ty:?}"
596                );
597
598                // Storage images cannot be directly compared since while the access is part of the
599                // type in naga's IR, in glsl they are a qualifier and don't enter in the match as
600                // long as the access needed is satisfied.
601                if let (
602                    &TypeInner::Image {
603                        class:
604                            crate::ImageClass::Storage {
605                                format: overload_format,
606                                access: overload_access,
607                            },
608                        dim: overload_dim,
609                        arrayed: overload_arrayed,
610                    },
611                    &TypeInner::Image {
612                        class:
613                            crate::ImageClass::Storage {
614                                format: call_format,
615                                access: call_access,
616                            },
617                        dim: call_dim,
618                        arrayed: call_arrayed,
619                    },
620                ) = (overload_param_ty, call_arg_ty)
621                {
622                    // Images size must match otherwise the overload isn't what we want
623                    let good_size = call_dim == overload_dim && call_arrayed == overload_arrayed;
624                    // Glsl requires the formats to strictly match unless you are builtin
625                    // function overload and have not been replaced, in which case we only
626                    // check that the format scalar kind matches
627                    let good_format = overload_format == call_format
628                        || (overload.internal
629                            && Scalar::from(overload_format) == Scalar::from(call_format));
630                    if !(good_size && good_format) {
631                        continue 'outer;
632                    }
633
634                    // While storage access mismatch is an error it isn't one that causes
635                    // the overload matching to fail so we defer the error and consider
636                    // that the images match exactly
637                    if !call_access.contains(overload_access) {
638                        self.errors.push(Error {
639                            kind: ErrorKind::SemanticError(
640                                format!(
641                                    "'{name}': image needs {overload_access:?} access but only {call_access:?} was provided"
642                                )
643                                .into(),
644                            ),
645                            meta,
646                        });
647                    }
648
649                    // The images satisfy the conditions to be considered as an exact match
650                    new_conversions[i] = Conversion::Exact;
651                    continue;
652                } else if overload_param_ty == call_arg_ty {
653                    // If the types match there's no need to check for conversions so continue
654                    new_conversions[i] = Conversion::Exact;
655                    continue;
656                }
657
658                // Glsl defines that inout follows both the conversions for input parameters and
659                // output parameters, this means that the type must have a conversion from both the
660                // call argument to the function parameter and the function parameter to the call
661                // argument, the only way this is possible is for the conversion to be an identity
662                // (i.e. call argument = function parameter)
663                if let ParameterQualifier::InOut = parameter_info.qualifier {
664                    continue 'outer;
665                }
666
667                // The function call argument and the function definition
668                // parameter are not equal at this point, so we need to try
669                // implicit conversions.
670                //
671                // Now there are two cases, the argument is defined as a normal
672                // parameter (`in` or `const`), in this case an implicit
673                // conversion is made from the calling argument to the
674                // definition argument. If the parameter is `out` the
675                // opposite needs to be done, so the implicit conversion is made
676                // from the definition argument to the calling argument.
677                let maybe_conversion = if parameter_info.qualifier.is_lhs() {
678                    conversion(call_arg_ty, overload_param_ty)
679                } else {
680                    conversion(overload_param_ty, call_arg_ty)
681                };
682
683                let conversion = match maybe_conversion {
684                    Some(info) => info,
685                    None => continue 'outer,
686                };
687
688                // At this point a conversion will be needed so the overload no longer
689                // exactly matches the call arguments
690                exact = false;
691
692                // Compare the conversions needed for this overload parameter to that of the
693                // last overload analyzed respective parameter, the value is:
694                // - `true` when the new overload argument has a better conversion
695                // - `false` when the old overload argument has a better conversion
696                let best_arg = match (conversion, old_conversions[i]) {
697                    // An exact match is always better, we don't need to check this for the
698                    // current overload since it was checked earlier
699                    (_, Conversion::Exact) => false,
700                    // No overload was yet analyzed so this one is the best yet
701                    (_, Conversion::None) => true,
702                    // A conversion from a float to a double is the best possible conversion
703                    (Conversion::FloatToDouble, _) => true,
704                    (_, Conversion::FloatToDouble) => false,
705                    // A conversion from a float to an integer is preferred than one
706                    // from double to an integer
707                    (Conversion::IntToFloat, Conversion::IntToDouble) => true,
708                    (Conversion::IntToDouble, Conversion::IntToFloat) => false,
709                    // This case handles things like no conversion and exact which were already
710                    // treated and other cases which no conversion is better than the other
711                    _ => continue,
712                };
713
714                // Check if the best parameter corresponds to the current selected overload
715                // to pass to the next comparison, if this isn't true mark it as ambiguous
716                match best_arg {
717                    true => match superior {
718                        Some(false) => ambiguous = true,
719                        _ => {
720                            superior = Some(true);
721                            new_conversions[i] = conversion
722                        }
723                    },
724                    false => match superior {
725                        Some(true) => ambiguous = true,
726                        _ => superior = Some(false),
727                    },
728                }
729            }
730
731            // The overload matches exactly the function call so there's no ambiguity (since
732            // repeated overload aren't allowed) and the current overload is selected, no
733            // further querying is needed.
734            if exact {
735                maybe_overload = Some(overload);
736                ambiguous = false;
737                break;
738            }
739
740            match superior {
741                // New overload is better keep it
742                Some(true) => {
743                    maybe_overload = Some(overload);
744                    // Replace the conversions
745                    old_conversions = new_conversions;
746                }
747                // Old overload is better do nothing
748                Some(false) => {}
749                // No overload was better than the other this can be caused
750                // when all conversions are ambiguous in which the overloads themselves are
751                // ambiguous.
752                None => {
753                    ambiguous = true;
754                    // Assign the new overload, this helps ensures that in this case of
755                    // ambiguity the parsing won't end immediately and allow for further
756                    // collection of errors.
757                    maybe_overload = Some(overload);
758                }
759            }
760        }
761
762        if ambiguous {
763            self.errors.push(Error {
764                kind: ErrorKind::SemanticError(
765                    format!("Ambiguous best function for '{name}'").into(),
766                ),
767                meta,
768            })
769        }
770
771        let overload = maybe_overload.ok_or_else(|| Error {
772            kind: ErrorKind::SemanticError(format!("Unknown function '{name}'").into()),
773            meta,
774        })?;
775
776        let parameters_info = overload.parameters_info.clone();
777        let parameters = overload.parameters.clone();
778        let is_void = overload.void;
779        let kind = overload.kind;
780
781        let mut arguments = Vec::with_capacity(args.len());
782        let mut proxy_writes = Vec::new();
783
784        // Iterate through the function call arguments applying transformations as needed
785        for (((parameter_info, call_argument), expr), parameter) in parameters_info
786            .iter()
787            .zip(&args)
788            .zip(raw_args)
789            .zip(&parameters)
790        {
791            if parameter_info.qualifier.is_lhs() {
792                // Reprocess argument in LHS position
793                let (handle, meta) = ctx.lower_expect_inner(stmt, self, *expr, ExprPos::Lhs)?;
794
795                self.process_lhs_argument(
796                    ctx,
797                    meta,
798                    *parameter,
799                    parameter_info,
800                    handle,
801                    call_argument,
802                    &mut proxy_writes,
803                    &mut arguments,
804                )?;
805
806                continue;
807            }
808
809            let (mut handle, meta) = *call_argument;
810
811            let scalar_comps = scalar_components(&ctx.module.types[*parameter].inner);
812
813            // Apply implicit conversions as needed
814            if let Some(scalar) = scalar_comps {
815                ctx.implicit_conversion(&mut handle, meta, scalar)?;
816            }
817
818            arguments.push(handle)
819        }
820
821        match kind {
822            FunctionKind::Call(function) => {
823                ctx.emit_end();
824
825                let result = if !is_void {
826                    Some(ctx.add_expression(Expression::CallResult(function), meta)?)
827                } else {
828                    None
829                };
830
831                ctx.body.push(
832                    Statement::Call {
833                        function,
834                        arguments,
835                        result,
836                    },
837                    meta,
838                );
839
840                ctx.emit_start();
841
842                // Write back all the variables that were scheduled to their original place
843                for proxy_write in proxy_writes {
844                    let mut value = ctx.add_expression(
845                        Expression::Load {
846                            pointer: proxy_write.value,
847                        },
848                        meta,
849                    )?;
850
851                    if let Some(scalar) = proxy_write.convert {
852                        ctx.conversion(&mut value, meta, scalar)?;
853                    }
854
855                    ctx.emit_restart();
856
857                    ctx.body.push(
858                        Statement::Store {
859                            pointer: proxy_write.target,
860                            value,
861                        },
862                        meta,
863                    );
864                }
865
866                Ok(result)
867            }
868            FunctionKind::Macro(builtin) => builtin.call(self, ctx, arguments.as_mut_slice(), meta),
869        }
870    }
871
872    /// Processes a function call argument that appears in place of an output
873    /// parameter.
874    #[allow(clippy::too_many_arguments)]
875    fn process_lhs_argument(
876        &mut self,
877        ctx: &mut Context,
878        meta: Span,
879        parameter_ty: Handle<Type>,
880        parameter_info: &ParameterInfo,
881        original: Handle<Expression>,
882        call_argument: &(Handle<Expression>, Span),
883        proxy_writes: &mut Vec<ProxyWrite>,
884        arguments: &mut Vec<Handle<Expression>>,
885    ) -> Result<()> {
886        let original_ty = ctx.resolve_type(original, meta)?;
887        let original_pointer_space = original_ty.pointer_space();
888
889        // The type of a possible spill variable needed for a proxy write
890        let mut maybe_ty = match *original_ty {
891            // If the argument is to be passed as a pointer but the type of the
892            // expression returns a vector it must mean that it was for example
893            // swizzled and it must be spilled into a local before calling
894            TypeInner::Vector { size, scalar } => Some(ctx.module.types.insert(
895                Type {
896                    name: None,
897                    inner: TypeInner::Vector { size, scalar },
898                },
899                Span::default(),
900            )),
901            // If the argument is a pointer whose address space isn't `Function`, an
902            // indirection through a local variable is needed to align the address
903            // spaces of the call argument and the overload parameter.
904            TypeInner::Pointer { base, space } if space != AddressSpace::Function => Some(base),
905            TypeInner::ValuePointer {
906                size,
907                scalar,
908                space,
909            } if space != AddressSpace::Function => {
910                let inner = match size {
911                    Some(size) => TypeInner::Vector { size, scalar },
912                    None => TypeInner::Scalar(scalar),
913                };
914
915                Some(
916                    ctx.module
917                        .types
918                        .insert(Type { name: None, inner }, Span::default()),
919                )
920            }
921            _ => None,
922        };
923
924        // Since the original expression might be a pointer and we want a value
925        // for the proxy writes, we might need to load the pointer.
926        let value = if original_pointer_space.is_some() {
927            ctx.add_expression(Expression::Load { pointer: original }, Span::default())?
928        } else {
929            original
930        };
931
932        ctx.typifier_grow(call_argument.0, call_argument.1)?;
933
934        let overload_param_ty = &ctx.module.types[parameter_ty].inner;
935        let call_arg_ty = ctx.get_type(call_argument.0);
936        let needs_conversion = call_arg_ty != overload_param_ty;
937
938        let arg_scalar_comps = scalar_components(call_arg_ty);
939
940        // Since output parameters also allow implicit conversions from the
941        // parameter to the argument, we need to spill the conversion to a
942        // variable and create a proxy write for the original variable.
943        if needs_conversion {
944            maybe_ty = Some(parameter_ty);
945        }
946
947        if let Some(ty) = maybe_ty {
948            // Create the spill variable
949            let spill_var = ctx.locals.append(
950                LocalVariable {
951                    name: None,
952                    ty,
953                    init: None,
954                },
955                Span::default(),
956            );
957            let spill_expr =
958                ctx.add_expression(Expression::LocalVariable(spill_var), Span::default())?;
959
960            // If the argument is also copied in we must store the value of the
961            // original variable to the spill variable.
962            if let ParameterQualifier::InOut = parameter_info.qualifier {
963                ctx.body.push(
964                    Statement::Store {
965                        pointer: spill_expr,
966                        value,
967                    },
968                    Span::default(),
969                );
970            }
971
972            // Add the spill variable as an argument to the function call
973            arguments.push(spill_expr);
974
975            let convert = if needs_conversion {
976                arg_scalar_comps
977            } else {
978                None
979            };
980
981            // Register the temporary local to be written back to it's original
982            // place after the function call
983            if let Expression::Swizzle {
984                size,
985                mut vector,
986                pattern,
987            } = ctx.expressions[original]
988            {
989                if let Expression::Load { pointer } = ctx.expressions[vector] {
990                    vector = pointer;
991                }
992
993                for (i, component) in pattern.iter().take(size as usize).enumerate() {
994                    let original = ctx.add_expression(
995                        Expression::AccessIndex {
996                            base: vector,
997                            index: *component as u32,
998                        },
999                        Span::default(),
1000                    )?;
1001
1002                    let spill_component = ctx.add_expression(
1003                        Expression::AccessIndex {
1004                            base: spill_expr,
1005                            index: i as u32,
1006                        },
1007                        Span::default(),
1008                    )?;
1009
1010                    proxy_writes.push(ProxyWrite {
1011                        target: original,
1012                        value: spill_component,
1013                        convert,
1014                    });
1015                }
1016            } else {
1017                proxy_writes.push(ProxyWrite {
1018                    target: original,
1019                    value: spill_expr,
1020                    convert,
1021                });
1022            }
1023        } else {
1024            arguments.push(original);
1025        }
1026
1027        Ok(())
1028    }
1029
1030    pub(crate) fn add_function(
1031        &mut self,
1032        mut ctx: Context,
1033        name: String,
1034        result: Option<FunctionResult>,
1035        meta: Span,
1036    ) {
1037        ensure_block_returns(&mut ctx.body);
1038
1039        let void = result.is_none();
1040
1041        // Check if the passed arguments require any special variations
1042        let mut variations = builtin_required_variations(
1043            ctx.parameters
1044                .iter()
1045                .map(|&arg| &ctx.module.types[arg].inner),
1046        );
1047
1048        // Initiate the declaration if it wasn't previously initialized and inject builtins
1049        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {
1050            variations |= BuiltinVariations::STANDARD;
1051            Default::default()
1052        });
1053        inject_builtin(declaration, ctx.module, &name, variations);
1054
1055        let Context {
1056            expressions,
1057            locals,
1058            arguments,
1059            parameters,
1060            parameters_info,
1061            body,
1062            module,
1063            ..
1064        } = ctx;
1065
1066        let function = Function {
1067            name: Some(name),
1068            arguments,
1069            result,
1070            local_variables: locals,
1071            expressions,
1072            named_expressions: crate::NamedExpressions::default(),
1073            body,
1074            diagnostic_filter_leaf: None,
1075        };
1076
1077        'outer: for decl in declaration.overloads.iter_mut() {
1078            if parameters.len() != decl.parameters.len() {
1079                continue;
1080            }
1081
1082            for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter()) {
1083                let new_inner = &module.types[*new_parameter].inner;
1084                let old_inner = &module.types[*old_parameter].inner;
1085
1086                if new_inner != old_inner {
1087                    continue 'outer;
1088                }
1089            }
1090
1091            if decl.defined {
1092                return self.errors.push(Error {
1093                    kind: ErrorKind::SemanticError("Function already defined".into()),
1094                    meta,
1095                });
1096            }
1097
1098            decl.defined = true;
1099            decl.parameters_info = parameters_info;
1100            match decl.kind {
1101                FunctionKind::Call(handle) => *module.functions.get_mut(handle) = function,
1102                FunctionKind::Macro(_) => {
1103                    let handle = module.functions.append(function, meta);
1104                    decl.kind = FunctionKind::Call(handle)
1105                }
1106            }
1107            return;
1108        }
1109
1110        let handle = module.functions.append(function, meta);
1111        declaration.overloads.push(Overload {
1112            parameters,
1113            parameters_info,
1114            kind: FunctionKind::Call(handle),
1115            defined: true,
1116            internal: false,
1117            void,
1118        });
1119    }
1120
1121    pub(crate) fn add_prototype(
1122        &mut self,
1123        ctx: Context,
1124        name: String,
1125        result: Option<FunctionResult>,
1126        meta: Span,
1127    ) {
1128        let void = result.is_none();
1129
1130        // Check if the passed arguments require any special variations
1131        let mut variations = builtin_required_variations(
1132            ctx.parameters
1133                .iter()
1134                .map(|&arg| &ctx.module.types[arg].inner),
1135        );
1136
1137        // Initiate the declaration if it wasn't previously initialized and inject builtins
1138        let declaration = self.lookup_function.entry(name.clone()).or_insert_with(|| {
1139            variations |= BuiltinVariations::STANDARD;
1140            Default::default()
1141        });
1142        inject_builtin(declaration, ctx.module, &name, variations);
1143
1144        let Context {
1145            arguments,
1146            parameters,
1147            parameters_info,
1148            module,
1149            ..
1150        } = ctx;
1151
1152        let function = Function {
1153            name: Some(name),
1154            arguments,
1155            result,
1156            ..Default::default()
1157        };
1158
1159        'outer: for decl in declaration.overloads.iter() {
1160            if parameters.len() != decl.parameters.len() {
1161                continue;
1162            }
1163
1164            for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter()) {
1165                let new_inner = &module.types[*new_parameter].inner;
1166                let old_inner = &module.types[*old_parameter].inner;
1167
1168                if new_inner != old_inner {
1169                    continue 'outer;
1170                }
1171            }
1172
1173            return self.errors.push(Error {
1174                kind: ErrorKind::SemanticError("Prototype already defined".into()),
1175                meta,
1176            });
1177        }
1178
1179        let handle = module.functions.append(function, meta);
1180        declaration.overloads.push(Overload {
1181            parameters,
1182            parameters_info,
1183            kind: FunctionKind::Call(handle),
1184            defined: false,
1185            internal: false,
1186            void,
1187        });
1188    }
1189
1190    /// Create a Naga [`EntryPoint`] that calls the GLSL `main` function.
1191    ///
1192    /// We compile the GLSL `main` function as an ordinary Naga [`Function`].
1193    /// This function synthesizes a Naga [`EntryPoint`] to call that.
1194    ///
1195    /// Each GLSL input and output variable (including builtins) becomes a Naga
1196    /// [`GlobalVariable`]s in the [`Private`] address space, which `main` can
1197    /// access in the usual way.
1198    ///
1199    /// The `EntryPoint` we synthesize here has an argument for each GLSL input
1200    /// variable, and returns a struct with a member for each GLSL output
1201    /// variable. The entry point contains code to:
1202    ///
1203    /// - copy its arguments into the Naga globals representing the GLSL input
1204    ///   variables,
1205    ///
1206    /// - call the Naga `Function` representing the GLSL `main` function, and then
1207    ///
1208    /// - build its return value from whatever values the GLSL `main` left in
1209    ///   the Naga globals representing GLSL `output` variables.
1210    ///
1211    /// Upon entry, [`ctx.body`] should contain code, accumulated by prior calls
1212    /// to [`ParsingContext::parse_external_declaration`][pxd], to initialize
1213    /// private global variables as needed. This code gets spliced into the
1214    /// entry point before the call to `main`.
1215    ///
1216    /// [`GlobalVariable`]: crate::GlobalVariable
1217    /// [`Private`]: crate::AddressSpace::Private
1218    /// [`ctx.body`]: Context::body
1219    /// [pxd]: super::ParsingContext::parse_external_declaration
1220    pub(crate) fn add_entry_point(
1221        &mut self,
1222        function: Handle<Function>,
1223        mut ctx: Context,
1224    ) -> Result<()> {
1225        let mut arguments = Vec::new();
1226
1227        let body = Block::with_capacity(
1228            // global init body
1229            ctx.body.len() +
1230            // prologue and epilogue
1231            self.entry_args.len() * 2
1232            // Call, Emit for composing struct and return
1233            + 3,
1234        );
1235
1236        let global_init_body = core::mem::replace(&mut ctx.body, body);
1237
1238        for arg in self.entry_args.iter() {
1239            if arg.storage != StorageQualifier::Input {
1240                continue;
1241            }
1242
1243            let pointer = ctx
1244                .expressions
1245                .append(Expression::GlobalVariable(arg.handle), Default::default());
1246            ctx.local_expression_kind_tracker
1247                .insert(pointer, crate::proc::ExpressionKind::Runtime);
1248
1249            let ty = ctx.module.global_variables[arg.handle].ty;
1250
1251            ctx.arg_type_walker(
1252                arg.name.clone(),
1253                arg.binding.clone(),
1254                pointer,
1255                ty,
1256                &mut |ctx, name, pointer, ty, binding| {
1257                    let idx = arguments.len() as u32;
1258
1259                    arguments.push(FunctionArgument {
1260                        name,
1261                        ty,
1262                        binding: Some(binding),
1263                    });
1264
1265                    let value = ctx
1266                        .expressions
1267                        .append(Expression::FunctionArgument(idx), Default::default());
1268                    ctx.local_expression_kind_tracker
1269                        .insert(value, crate::proc::ExpressionKind::Runtime);
1270                    ctx.body
1271                        .push(Statement::Store { pointer, value }, Default::default());
1272                },
1273            )?
1274        }
1275
1276        ctx.body.extend_block(global_init_body);
1277
1278        ctx.body.push(
1279            Statement::Call {
1280                function,
1281                arguments: Vec::new(),
1282                result: None,
1283            },
1284            Default::default(),
1285        );
1286
1287        let mut span = 0;
1288        let mut members = Vec::new();
1289        let mut components = Vec::new();
1290
1291        for arg in self.entry_args.iter() {
1292            if arg.storage != StorageQualifier::Output {
1293                continue;
1294            }
1295
1296            let pointer = ctx
1297                .expressions
1298                .append(Expression::GlobalVariable(arg.handle), Default::default());
1299            ctx.local_expression_kind_tracker
1300                .insert(pointer, crate::proc::ExpressionKind::Runtime);
1301
1302            let ty = ctx.module.global_variables[arg.handle].ty;
1303
1304            ctx.arg_type_walker(
1305                arg.name.clone(),
1306                arg.binding.clone(),
1307                pointer,
1308                ty,
1309                &mut |ctx, name, pointer, ty, binding| {
1310                    members.push(StructMember {
1311                        name,
1312                        ty,
1313                        binding: Some(binding),
1314                        offset: span,
1315                    });
1316
1317                    span += ctx.module.types[ty].inner.size(ctx.module.to_ctx());
1318
1319                    let len = ctx.expressions.len();
1320                    let load = ctx
1321                        .expressions
1322                        .append(Expression::Load { pointer }, Default::default());
1323                    ctx.local_expression_kind_tracker
1324                        .insert(load, crate::proc::ExpressionKind::Runtime);
1325                    ctx.body.push(
1326                        Statement::Emit(ctx.expressions.range_from(len)),
1327                        Default::default(),
1328                    );
1329                    components.push(load)
1330                },
1331            )?
1332        }
1333
1334        let (ty, value) = if !components.is_empty() {
1335            let ty = ctx.module.types.insert(
1336                Type {
1337                    name: None,
1338                    inner: TypeInner::Struct { members, span },
1339                },
1340                Default::default(),
1341            );
1342
1343            let len = ctx.expressions.len();
1344            let res = ctx
1345                .expressions
1346                .append(Expression::Compose { ty, components }, Default::default());
1347            ctx.local_expression_kind_tracker
1348                .insert(res, crate::proc::ExpressionKind::Runtime);
1349            ctx.body.push(
1350                Statement::Emit(ctx.expressions.range_from(len)),
1351                Default::default(),
1352            );
1353
1354            (Some(ty), Some(res))
1355        } else {
1356            (None, None)
1357        };
1358
1359        ctx.body
1360            .push(Statement::Return { value }, Default::default());
1361
1362        let Context {
1363            body, expressions, ..
1364        } = ctx;
1365
1366        ctx.module.entry_points.push(EntryPoint {
1367            name: "main".to_string(),
1368            stage: self.meta.stage,
1369            early_depth_test: Some(crate::EarlyDepthTest::Force)
1370                .filter(|_| self.meta.early_fragment_tests),
1371            workgroup_size: self.meta.workgroup_size,
1372            workgroup_size_overrides: None,
1373            function: Function {
1374                arguments,
1375                expressions,
1376                body,
1377                result: ty.map(|ty| FunctionResult { ty, binding: None }),
1378                ..Default::default()
1379            },
1380        });
1381
1382        Ok(())
1383    }
1384}
1385
1386impl Context<'_> {
1387    /// Helper function for building the input/output interface of the entry point
1388    ///
1389    /// Calls `f` with the data of the entry point argument, flattening composite types
1390    /// recursively
1391    ///
1392    /// The passed arguments to the callback are:
1393    /// - The ctx
1394    /// - The name
1395    /// - The pointer expression to the global storage
1396    /// - The handle to the type of the entry point argument
1397    /// - The binding of the entry point argument
1398    fn arg_type_walker(
1399        &mut self,
1400        name: Option<String>,
1401        binding: crate::Binding,
1402        pointer: Handle<Expression>,
1403        ty: Handle<Type>,
1404        f: &mut impl FnMut(
1405            &mut Context,
1406            Option<String>,
1407            Handle<Expression>,
1408            Handle<Type>,
1409            crate::Binding,
1410        ),
1411    ) -> Result<()> {
1412        match self.module.types[ty].inner {
1413            // TODO: Better error reporting
1414            // right now we just don't walk the array if the size isn't known at
1415            // compile time and let validation catch it
1416            TypeInner::Array {
1417                base,
1418                size: crate::ArraySize::Constant(size),
1419                ..
1420            } => {
1421                let mut location = match binding {
1422                    crate::Binding::Location { location, .. } => location,
1423                    crate::Binding::BuiltIn(_) => return Ok(()),
1424                };
1425
1426                let interpolation =
1427                    self.module.types[base]
1428                        .inner
1429                        .scalar_kind()
1430                        .map(|kind| match kind {
1431                            ScalarKind::Float => crate::Interpolation::Perspective,
1432                            _ => crate::Interpolation::Flat,
1433                        });
1434
1435                for index in 0..size.get() {
1436                    let member_pointer = self.add_expression(
1437                        Expression::AccessIndex {
1438                            base: pointer,
1439                            index,
1440                        },
1441                        Span::default(),
1442                    )?;
1443
1444                    let binding = crate::Binding::Location {
1445                        location,
1446                        interpolation,
1447                        sampling: None,
1448                        blend_src: None,
1449                    };
1450                    location += 1;
1451
1452                    self.arg_type_walker(name.clone(), binding, member_pointer, base, f)?
1453                }
1454            }
1455            TypeInner::Struct { ref members, .. } => {
1456                let mut location = match binding {
1457                    crate::Binding::Location { location, .. } => location,
1458                    crate::Binding::BuiltIn(_) => return Ok(()),
1459                };
1460
1461                for (i, member) in members.clone().into_iter().enumerate() {
1462                    let member_pointer = self.add_expression(
1463                        Expression::AccessIndex {
1464                            base: pointer,
1465                            index: i as u32,
1466                        },
1467                        Span::default(),
1468                    )?;
1469
1470                    let binding = match member.binding {
1471                        Some(binding) => binding,
1472                        None => {
1473                            let interpolation = self.module.types[member.ty]
1474                                .inner
1475                                .scalar_kind()
1476                                .map(|kind| match kind {
1477                                    ScalarKind::Float => crate::Interpolation::Perspective,
1478                                    _ => crate::Interpolation::Flat,
1479                                });
1480                            let binding = crate::Binding::Location {
1481                                location,
1482                                interpolation,
1483                                sampling: None,
1484                                blend_src: None,
1485                            };
1486                            location += 1;
1487                            binding
1488                        }
1489                    };
1490
1491                    self.arg_type_walker(member.name, binding, member_pointer, member.ty, f)?
1492                }
1493            }
1494            _ => f(self, name, pointer, ty, binding),
1495        }
1496
1497        Ok(())
1498    }
1499}
1500
1501/// Helper enum containing the type of conversion need for a call
1502#[derive(PartialEq, Eq, Clone, Copy, Debug)]
1503enum Conversion {
1504    /// No conversion needed
1505    Exact,
1506    /// Float to double conversion needed
1507    FloatToDouble,
1508    /// Int or uint to float conversion needed
1509    IntToFloat,
1510    /// Int or uint to double conversion needed
1511    IntToDouble,
1512    /// Other type of conversion needed
1513    Other,
1514    /// No conversion was yet registered
1515    None,
1516}
1517
1518/// Helper function, returns the type of conversion from `source` to `target`, if a
1519/// conversion is not possible returns None.
1520fn conversion(target: &TypeInner, source: &TypeInner) -> Option<Conversion> {
1521    use ScalarKind::*;
1522
1523    // Gather the `ScalarKind` and scalar width from both the target and the source
1524    let (target_scalar, source_scalar) = match (target, source) {
1525        // Conversions between scalars are allowed
1526        (&TypeInner::Scalar(tgt_scalar), &TypeInner::Scalar(src_scalar)) => {
1527            (tgt_scalar, src_scalar)
1528        }
1529        // Conversions between vectors of the same size are allowed
1530        (
1531            &TypeInner::Vector {
1532                size: tgt_size,
1533                scalar: tgt_scalar,
1534            },
1535            &TypeInner::Vector {
1536                size: src_size,
1537                scalar: src_scalar,
1538            },
1539        ) if tgt_size == src_size => (tgt_scalar, src_scalar),
1540        // Conversions between matrices of the same size are allowed
1541        (
1542            &TypeInner::Matrix {
1543                rows: tgt_rows,
1544                columns: tgt_cols,
1545                scalar: tgt_scalar,
1546            },
1547            &TypeInner::Matrix {
1548                rows: src_rows,
1549                columns: src_cols,
1550                scalar: src_scalar,
1551            },
1552        ) if tgt_cols == src_cols && tgt_rows == src_rows => (tgt_scalar, src_scalar),
1553        _ => return None,
1554    };
1555
1556    // Check if source can be converted into target, if this is the case then the type
1557    // power of target must be higher than that of source
1558    let target_power = type_power(target_scalar);
1559    let source_power = type_power(source_scalar);
1560    if target_power < source_power {
1561        return None;
1562    }
1563
1564    Some(match (target_scalar, source_scalar) {
1565        // A conversion from a float to a double is special
1566        (Scalar::F64, Scalar::F32) => Conversion::FloatToDouble,
1567        // A conversion from an integer to a float is special
1568        (
1569            Scalar::F32,
1570            Scalar {
1571                kind: Sint | Uint,
1572                width: _,
1573            },
1574        ) => Conversion::IntToFloat,
1575        // A conversion from an integer to a double is special
1576        (
1577            Scalar::F64,
1578            Scalar {
1579                kind: Sint | Uint,
1580                width: _,
1581            },
1582        ) => Conversion::IntToDouble,
1583        _ => Conversion::Other,
1584    })
1585}
1586
1587/// Helper method returning all the non standard builtin variations needed
1588/// to process the function call with the passed arguments
1589fn builtin_required_variations<'a>(args: impl Iterator<Item = &'a TypeInner>) -> BuiltinVariations {
1590    let mut variations = BuiltinVariations::empty();
1591
1592    for ty in args {
1593        match *ty {
1594            TypeInner::ValuePointer { scalar, .. }
1595            | TypeInner::Scalar(scalar)
1596            | TypeInner::Vector { scalar, .. }
1597            | TypeInner::Matrix { scalar, .. } => {
1598                if scalar == Scalar::F64 {
1599                    variations |= BuiltinVariations::DOUBLE
1600                }
1601            }
1602            TypeInner::Image {
1603                dim,
1604                arrayed,
1605                class,
1606            } => {
1607                if dim == crate::ImageDimension::Cube && arrayed {
1608                    variations |= BuiltinVariations::CUBE_TEXTURES_ARRAY
1609                }
1610
1611                if dim == crate::ImageDimension::D2 && arrayed && class.is_multisampled() {
1612                    variations |= BuiltinVariations::D2_MULTI_TEXTURES_ARRAY
1613                }
1614            }
1615            _ => {}
1616        }
1617    }
1618
1619    variations
1620}