Skip to content

Commit e7f8127

Browse files
committed
[naga] Support MathFunction overloads correctly.
Define a new trait, `proc::builtins::OverloadSet`, for types that represent a Naga IR builtin function's set of overloads. The `OverloadSet` trait includes operations needed to validate calls, choose automatic type conversions, and generate diagnostics. Add a new function, `ir::MathFunction::overloads`, which returns the given `MathFunction`'s set of overloads as an `impl OverloadSet` value. Use this in the WGSL front end, the validator, and the typifier. To support `MathFunction::overloads`, provide two implementations of `OverloadSet`: - `List` is flexible but verbose. - `Regular` is concise but more restrictive. Some snapshot output is affected because `TypeResolution::Handle` values turn into `TypeResolution::Value`, since the function database constructs the return type directly. To work around #7405, avoid offering abstract-typed overloads of some functions. This addresses #6443 for `MathFunction`, although that issue covers other categories of operations as well.
1 parent 2cf5c4f commit e7f8127

20 files changed

+2681
-904
lines changed

naga/src/common/diagnostic_display.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Displaying Naga IR terms in diagnostic output.
22
3-
use crate::proc::GlobalCtx;
3+
use crate::proc::{GlobalCtx, Rule};
44
use crate::{Handle, Scalar, Type, TypeInner};
55

66
#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
@@ -83,6 +83,23 @@ impl fmt::Display for DiagnosticDisplay<(&TypeInner, GlobalCtx<'_>)> {
8383
}
8484
}
8585

86+
impl fmt::Display for DiagnosticDisplay<(&str, &Rule, GlobalCtx<'_>)> {
87+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88+
let (name, rule, ref ctx) = self.0;
89+
90+
#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
91+
ctx.write_type_rule(name, rule, f)?;
92+
93+
#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
94+
{
95+
let _ = ctx;
96+
write!(f, "{name}({:?}) -> {:?}", rule.arguments, rule.conclusion)?;
97+
}
98+
99+
Ok(())
100+
}
101+
}
102+
86103
impl fmt::Display for DiagnosticDisplay<Scalar> {
87104
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88105
let scalar = self.0;

naga/src/common/wgsl/types.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,37 @@ pub trait TypeContext {
133133
}
134134
}
135135

136+
fn write_type_conclusion<W: Write>(
137+
&self,
138+
conclusion: &crate::proc::Conclusion,
139+
out: &mut W,
140+
) -> core::fmt::Result {
141+
use crate::proc::Conclusion as Co;
142+
143+
match *conclusion {
144+
Co::Value(ref inner) => self.write_type_inner(inner, out),
145+
Co::Predeclared(ref predeclared) => out.write_str(&predeclared.struct_name()),
146+
}
147+
}
148+
149+
fn write_type_rule<W: Write>(
150+
&self,
151+
name: &str,
152+
rule: &crate::proc::Rule,
153+
out: &mut W,
154+
) -> core::fmt::Result {
155+
write!(out, "fn {name}(")?;
156+
for (i, arg) in rule.arguments.iter().enumerate() {
157+
if i > 0 {
158+
out.write_str(", ")?;
159+
}
160+
self.write_type_resolution(arg, out)?
161+
}
162+
out.write_str(") -> ")?;
163+
self.write_type_conclusion(&rule.conclusion, out)?;
164+
Ok(())
165+
}
166+
136167
fn type_to_string(&self, handle: Handle<crate::Type>) -> String {
137168
let mut buf = String::new();
138169
self.write_type(handle, &mut buf).unwrap();
@@ -150,6 +181,12 @@ pub trait TypeContext {
150181
self.write_type_resolution(resolution, &mut buf).unwrap();
151182
buf
152183
}
184+
185+
fn type_rule_to_string(&self, name: &str, rule: &crate::proc::Rule) -> String {
186+
let mut buf = String::new();
187+
self.write_type_rule(name, rule, &mut buf).unwrap();
188+
buf
189+
}
153190
}
154191

155192
fn try_write_type_inner<C, W>(ctx: &C, inner: &TypeInner, out: &mut W) -> Result<(), WriteTypeError>

naga/src/front/wgsl/error.rs

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,79 @@ pub(crate) enum Error<'a> {
271271
expected: Range<u32>,
272272
found: u32,
273273
},
274+
/// No overload of this function accepts this many arguments.
275+
TooManyArguments {
276+
/// The name of the function being called.
277+
function: String,
278+
279+
/// The function name in the call expression.
280+
call_span: Span,
281+
282+
/// The first argument that is unacceptable.
283+
arg_span: Span,
284+
285+
/// Maximum number of arguments accepted by any overload of
286+
/// this function.
287+
max_arguments: u32,
288+
},
289+
/// A value passed to a builtin function has a type that is not
290+
/// accepted by any overload of the function.
291+
WrongArgumentType {
292+
/// The name of the function being called.
293+
function: String,
294+
295+
/// The function name in the call expression.
296+
call_span: Span,
297+
298+
/// The first argument whose type is unacceptable.
299+
arg_span: Span,
300+
301+
/// The index of the first argument whose type is unacceptable.
302+
arg_index: u32,
303+
304+
/// That argument's actual type.
305+
arg_ty: String,
306+
307+
/// The set of argument types that would have been accepted for
308+
/// this argument, given the prior arguments.
309+
allowed: Vec<String>,
310+
},
311+
/// A value passed to a builtin function has a type that is not
312+
/// accepted, given the earlier arguments' types.
313+
InconsistentArgumentType {
314+
/// The name of the function being called.
315+
function: String,
316+
317+
/// The function name in the call expression.
318+
call_span: Span,
319+
320+
/// The first unacceptable argument.
321+
arg_span: Span,
322+
323+
/// The index of the first unacceptable argument.
324+
arg_index: u32,
325+
326+
/// The actual type of the first unacceptable argument.
327+
arg_ty: String,
328+
329+
/// The prior argument whose type made the `arg_span` argument
330+
/// unacceptable.
331+
prior_span: Span,
332+
333+
/// The index of the `prior_arg_span` argument.
334+
prior_index: u32,
335+
336+
/// The type of the `prior_arg_span` argument.
337+
prior_ty: String,
338+
339+
/// The types that would have been accepted instead of the
340+
/// first unacceptable argument.
341+
allowed: Vec<String>,
342+
},
343+
AmbiguousCall {
344+
call_span: Span,
345+
alternatives: Vec<String>,
346+
},
274347
FunctionReturnsVoid(Span),
275348
FunctionMustUseUnused(Span),
276349
FunctionMustUseReturnsVoid(Span, Span),
@@ -402,7 +475,8 @@ impl<'a> Error<'a> {
402475
"workgroup size separator (`,`) or a closing parenthesis".to_string()
403476
}
404477
ExpectedToken::GlobalItem => concat!(
405-
"global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) ",
478+
"global item (`struct`, `const`, `var`, `alias`, ",
479+
"`fn`, `diagnostic`, `enable`, `requires`, `;`) ",
406480
"or the end of the file"
407481
)
408482
.to_string(),
@@ -831,6 +905,85 @@ impl<'a> Error<'a> {
831905
labels: vec![(span, "wrong number of arguments".into())],
832906
notes: vec![],
833907
},
908+
Error::TooManyArguments {
909+
ref function,
910+
call_span,
911+
arg_span,
912+
max_arguments,
913+
} => ParseError {
914+
message: format!("too many arguments passed to `{function}`"),
915+
labels: vec![
916+
(call_span, "".into()),
917+
(arg_span, format!("unexpected argument #{}", max_arguments + 1).into())
918+
],
919+
notes: vec![
920+
format!("The `{function}` function accepts at most {max_arguments} argument(s)")
921+
],
922+
},
923+
Error::WrongArgumentType {
924+
ref function,
925+
call_span,
926+
arg_span,
927+
arg_index,
928+
ref arg_ty,
929+
ref allowed,
930+
} => {
931+
let message = format!(
932+
"wrong type passed as argument #{} to `{function}`",
933+
arg_index + 1,
934+
);
935+
let labels = vec![
936+
(call_span, "".into()),
937+
(arg_span, format!("argument #{} has type `{arg_ty}`", arg_index + 1).into())
938+
];
939+
940+
let mut notes = vec![];
941+
notes.push(format!("`{function}` accepts the following types for argument #{}:", arg_index + 1));
942+
notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
943+
944+
ParseError { message, labels, notes }
945+
},
946+
Error::InconsistentArgumentType {
947+
ref function,
948+
call_span,
949+
arg_span,
950+
arg_index,
951+
ref arg_ty,
952+
prior_span,
953+
prior_index,
954+
ref prior_ty,
955+
ref allowed
956+
} => {
957+
let message = format!(
958+
"inconsistent type passed as argument #{} to `{function}`",
959+
arg_index + 1,
960+
);
961+
let labels = vec![
962+
(call_span, "".into()),
963+
(arg_span, format!("argument #{} has type {arg_ty}", arg_index + 1).into()),
964+
(prior_span, format!(
965+
"this argument has type {prior_ty}, which constrains subsequent arguments"
966+
).into()),
967+
];
968+
let mut notes = vec![
969+
format!("Because argument #{} has type {prior_ty}, only the following types", prior_index + 1),
970+
format!("(or types that automatically convert to them) are accepted for argument #{}:", arg_index + 1),
971+
];
972+
notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
973+
974+
ParseError { message, labels, notes }
975+
}
976+
Error::AmbiguousCall { call_span, ref alternatives } => {
977+
let message = "function call is ambiguous: more than one overload could apply".into();
978+
let labels = vec![
979+
(call_span, "more than one overload of this function could apply to these arguments".into()),
980+
];
981+
let mut notes = vec![
982+
"All of the following overloads could apply, but no one overload is clearly preferable:".into()
983+
];
984+
notes.extend(alternatives.iter().map(|alt| format!("possible overload: {alt}")));
985+
ParseError { message, labels, notes }
986+
},
834987
Error::FunctionReturnsVoid(span) => ParseError {
835988
message: "function does not return any value".to_string(),
836989
labels: vec![(span, "".into())],

0 commit comments

Comments
 (0)