diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index a131d8cab4..9587daac52 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -260,6 +260,13 @@ pub(crate) enum Error<'a> { expected: Range, found: u32, }, + WrongArgumentCountForOverloads { + function: String, + call_span: Span, + arg_span: Span, + max_arguments: u32, + overloads: Vec, + }, WrongArgumentType { function: String, call_span: Span, @@ -821,6 +828,27 @@ impl<'a> Error<'a> { labels: vec![(span, "wrong number of arguments".into())], notes: vec![], }, + Error::WrongArgumentCountForOverloads { + ref function, + call_span, + arg_span, + max_arguments, + ref overloads, + } => { + let message = format!( + "For the preceding argument types, `{function}` accepts only {max_arguments} arguments" + ); + let labels = vec![ + (call_span, "This function call has too many arguments".into()), + (arg_span, "This is the first excess argument".into()) + ]; + let mut notes = vec![ + format!("These are the only overloads of `{function}` that could accept the preceding arguments:"), + ]; + notes.extend(overloads.iter().map(|o| format!("overload: {o}"))); + + ParseError { message, labels, notes } + } Error::WrongArgumentType { ref function, call_span, diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs index 13f641b6a0..5a29529e38 100644 --- a/naga/src/front/wgsl/lower/mod.rs +++ b/naga/src/front/wgsl/lower/mod.rs @@ -2298,20 +2298,52 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { use crate::proc::Rule as _; let mut overloads = fun.overloads(); + log::debug!("JIMB: {fun:?}"); + let min_arguments = overloads.min_subexpressions(); + let max_arguments = overloads.max_subexpressions(); + if arguments.len() < min_arguments || max_arguments < arguments.len() { + return Err(Error::WrongArgumentCount { + span, + expected: min_arguments as u32 .. max_arguments as u32, + found: arguments.len() as u32 + }); + } + let mut unconverted_arguments = Vec::with_capacity(arguments.len()); for (i, &arg) in arguments.iter().enumerate() { let lowered = self.expression_for_abstract(arg, ctx)?; let ty = resolve_inner!(ctx, lowered); let remaining_overloads = overloads.arg(i, ty, &ctx.module.types); if remaining_overloads.is_empty() { - return Err(Error::WrongArgumentType { - function: fun.to_wgsl().to_string(), - call_span: span, - arg_span: ctx.ast_expressions.get_span(arg), - arg_index: i as u32, - found: crate::common::wgsl::Wgslish(ty).to_string(), - allowed: overloads.allowed_args(i), - }); + // If there were other types permitted for the i'th arg, then + // this is a no-such-overload error. + // + // Otherwise, since we checked the argument count against the + // min and max earlier, it must be the case that the prior + // arguments narrowed the overloads to a subset none of which + // accept this many arguments. + let arg_span = ctx.ast_expressions.get_span(arg); + let allowed = overloads.allowed_args(i); + let function = fun.to_wgsl().to_string(); + if allowed.is_empty() { + let overload_list = overloads.overload_list(&function); + return Err(Error::WrongArgumentCountForOverloads { + function, + call_span: span, + arg_span, + max_arguments: overloads.max_subexpressions() as u32, + overloads: overload_list, + }); + } else { + return Err(Error::WrongArgumentType { + function, + call_span: span, + arg_span, + arg_index: i as u32, + found: crate::common::wgsl::Wgslish(ty).to_string(), + allowed, + }); + } } overloads = remaining_overloads; unconverted_arguments.push(lowered); diff --git a/naga/src/proc/builtins/list.rs b/naga/src/proc/builtins/list.rs index 9a2addb01a..7eb485935a 100644 --- a/naga/src/proc/builtins/list.rs +++ b/naga/src/proc/builtins/list.rs @@ -84,16 +84,22 @@ impl super::OverloadSet for List { self.members == 0 } - fn min_subexpressions(&self) -> Option { - self.members().fold(None, |n, (_, rule)| { - std::cmp::min(n, Some(rule.subexpressions.len())) - }) + fn min_subexpressions(&self) -> usize { + self.members().fold(None, |best, (_, rule)| { + // This is different from `max_subexpressions` because + // `