Skip to main content

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