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