Skip to content

Commit 45b1e79

Browse files
jimblandyVecvec
authored andcommitted
[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 gfx-rs#7405, avoid offering abstract-typed overloads of some functions. This addresses gfx-rs#6443 for `MathFunction`, although that issue covers other categories of operations as well.
1 parent e1d1c10 commit 45b1e79

27 files changed

+3054
-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: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,75 @@ 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+
inconsistent_span: Span,
332+
333+
/// The index of the `inconsistent_span` argument.
334+
inconsistent_index: u32,
335+
336+
/// The type of the `inconsistent_span` argument.
337+
inconsistent_ty: String,
338+
339+
/// The types that would have been accepted instead of the
340+
/// first unacceptable argument.
341+
allowed: Vec<String>,
342+
},
274343
FunctionReturnsVoid(Span),
275344
FunctionMustUseUnused(Span),
276345
FunctionMustUseReturnsVoid(Span, Span),
@@ -402,7 +471,8 @@ impl<'a> Error<'a> {
402471
"workgroup size separator (`,`) or a closing parenthesis".to_string()
403472
}
404473
ExpectedToken::GlobalItem => concat!(
405-
"global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) ",
474+
"global item (`struct`, `const`, `var`, `alias`, ",
475+
"`fn`, `diagnostic`, `enable`, `requires`, `;`) ",
406476
"or the end of the file"
407477
)
408478
.to_string(),
@@ -831,6 +901,74 @@ impl<'a> Error<'a> {
831901
labels: vec![(span, "wrong number of arguments".into())],
832902
notes: vec![],
833903
},
904+
Error::TooManyArguments {
905+
ref function,
906+
call_span,
907+
arg_span,
908+
max_arguments,
909+
} => ParseError {
910+
message: format!("too many arguments passed to `{function}`"),
911+
labels: vec![
912+
(call_span, "".into()),
913+
(arg_span, format!("unexpected argument #{}", max_arguments + 1).into())
914+
],
915+
notes: vec![
916+
format!("The `{function}` function accepts at most {max_arguments} argument(s)")
917+
],
918+
},
919+
Error::WrongArgumentType {
920+
ref function,
921+
call_span,
922+
arg_span,
923+
arg_index,
924+
ref arg_ty,
925+
ref allowed,
926+
} => {
927+
let message = format!(
928+
"wrong type passed as argument #{} to `{function}`",
929+
arg_index + 1,
930+
);
931+
let labels = vec![
932+
(call_span, "".into()),
933+
(arg_span, format!("argument #{} has type `{arg_ty}`", arg_index + 1).into())
934+
];
935+
936+
let mut notes = vec![];
937+
notes.push(format!("`{function}` accepts the following types for argument #{}:", arg_index + 1));
938+
notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
939+
940+
ParseError { message, labels, notes }
941+
},
942+
Error::InconsistentArgumentType {
943+
ref function,
944+
call_span,
945+
arg_span,
946+
arg_index,
947+
ref arg_ty,
948+
inconsistent_span,
949+
inconsistent_index,
950+
ref inconsistent_ty,
951+
ref allowed
952+
} => {
953+
let message = format!(
954+
"inconsistent type passed as argument #{} to `{function}`",
955+
arg_index + 1,
956+
);
957+
let labels = vec![
958+
(call_span, "".into()),
959+
(arg_span, format!("argument #{} has type {arg_ty}", arg_index + 1).into()),
960+
(inconsistent_span, format!(
961+
"this argument has type {inconsistent_ty}, which constrains subsequent arguments"
962+
).into()),
963+
];
964+
let mut notes = vec![
965+
format!("Because argument #{} has type {inconsistent_ty}, only the following types", inconsistent_index + 1),
966+
format!("(or types that automatically convert to them) are accepted for argument #{}:", arg_index + 1),
967+
];
968+
notes.extend(allowed.iter().map(|ty| format!("allowed type: {ty}")));
969+
970+
ParseError { message, labels, notes }
971+
}
834972
Error::FunctionReturnsVoid(span) => ParseError {
835973
message: "function does not return any value".to_string(),
836974
labels: vec![(span, "".into())],

0 commit comments

Comments
 (0)