Allow unsigned integers in switch

This commit is contained in:
João Capucho
2021-09-19 22:31:38 +01:00
committed by Dzmitry Malyshau
parent 6a57559070
commit d5fc05e8a4
15 changed files with 204 additions and 142 deletions

View File

@@ -1427,11 +1427,18 @@ impl<'a, W: Write> Writer<'a, W> {
write!(self.out, "switch(")?;
self.write_expr(selector, ctx)?;
writeln!(self.out, ") {{")?;
let type_postfix = match *ctx.info[selector].ty.inner_with(&self.module.types) {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Uint,
..
} => "u",
_ => "",
};
// Write all cases
let l2 = level.next();
for case in cases {
writeln!(self.out, "{}case {}:", l2, case.value)?;
writeln!(self.out, "{}case {}{}:", l2, case.value, type_postfix)?;
for sta in case.body.iter() {
self.write_stmt(sta, ctx, l2.next())?;

View File

@@ -1370,13 +1370,24 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, "switch(")?;
self.write_expr(module, selector, func_ctx)?;
writeln!(self.out, ") {{")?;
let type_postfix = match *func_ctx.info[selector].ty.inner_with(&module.types) {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Uint,
..
} => "u",
_ => "",
};
// Write all cases
let indent_level_1 = level.next();
let indent_level_2 = indent_level_1.next();
for case in cases {
writeln!(self.out, "{}case {}: {{", indent_level_1, case.value)?;
writeln!(
self.out,
"{}case {}{}: {{",
indent_level_1, case.value, type_postfix
)?;
if case.fall_through {
// Generate each fallthrough case statement in a new block. This is done to

View File

@@ -1439,10 +1439,17 @@ impl<W: Write> Writer<W> {
} => {
write!(self.out, "{}switch(", level)?;
self.put_expression(selector, &context.expression, true)?;
let type_postfix = match *context.expression.resolve_type(selector) {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Uint,
..
} => "u",
_ => "",
};
writeln!(self.out, ") {{")?;
let lcase = level.next();
for case in cases.iter() {
writeln!(self.out, "{}case {}: {{", lcase, case.value)?;
writeln!(self.out, "{}case {}{}: {{", lcase, case.value, type_postfix)?;
self.put_block(lcase.next(), &case.body, context)?;
if !case.fall_through {
writeln!(self.out, "{}break;", lcase.next())?;

View File

@@ -887,6 +887,13 @@ impl<W: Write> Writer<W> {
let all_fall_through = cases
.iter()
.all(|case| case.fall_through && case.body.is_empty());
let type_postfix = match *func_ctx.info[selector].ty.inner_with(&module.types) {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Uint,
..
} => "u",
_ => "",
};
let l2 = level.next();
if !cases.is_empty() {
@@ -896,11 +903,11 @@ impl<W: Write> Writer<W> {
}
if !all_fall_through && case.fall_through && case.body.is_empty() {
write_case = false;
write!(self.out, "{}, ", case.value)?;
write!(self.out, "{}{}, ", case.value, type_postfix)?;
continue;
} else {
write_case = true;
writeln!(self.out, "{}: {{", case.value)?;
writeln!(self.out, "{}{}: {{", case.value, type_postfix)?;
}
for sta in case.body.iter() {

View File

@@ -157,19 +157,12 @@ impl<'source> ParsingContext<'source> {
self.expect(parser, TokenValue::LeftParen)?;
let (mut selector, selector_meta) = {
let selector = {
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(parser, ctx, &mut stmt, body)?;
ctx.lower_expect(stmt, parser, expr, ExprPos::Rhs, body)?
ctx.lower_expect(stmt, parser, expr, ExprPos::Rhs, body)?.0
};
if let Some(crate::ScalarKind::Uint) = parser
.resolve_type(ctx, selector, selector_meta)?
.scalar_kind()
{
ctx.conversion(&mut selector, selector_meta, crate::ScalarKind::Sint, 4)?
}
self.expect(parser, TokenValue::RightParen)?;
ctx.emit_flush(body);

View File

@@ -21,7 +21,7 @@ use self::{
lexer::Lexer,
number_literals::{
get_f32_literal, get_i32_literal, get_u32_literal, parse_generic_non_negative_int_literal,
parse_non_negative_sint_literal, parse_sint_literal,
parse_non_negative_sint_literal,
},
};
use codespan_reporting::{
@@ -83,6 +83,7 @@ pub enum ExpectedToken<'a> {
ty: Option<NumberType>,
width: Option<Bytes>,
},
Integer,
Constant,
/// Expected: constant, parenthesized expression, identifier
PrimaryExpression,
@@ -218,6 +219,7 @@ impl<'a> Error<'a> {
)
}
},
ExpectedToken::Integer => "unsigned/signed integer literal".to_string(),
ExpectedToken::Constant => "constant".to_string(),
ExpectedToken::PrimaryExpression => "expression".to_string(),
ExpectedToken::AttributeSeparator => "attribute separator (',') or an end of the attribute list (']]')".to_string(),
@@ -1242,6 +1244,28 @@ impl Parser {
})
}
fn parse_switch_value<'a>(lexer: &mut Lexer<'a>, uint: bool) -> Result<i32, Error<'a>> {
let token_span = lexer.next();
let word = match token_span.0 {
Token::Number { value, width, .. } => {
if let Some(width) = width {
if width != 4 {
// Only 32-bit literals supported by the spec and naga for now!
return Err(Error::BadScalarWidth(token_span.1, width));
}
}
value
}
_ => return Err(Error::Unexpected(token_span, ExpectedToken::Integer)),
};
match uint {
true => get_u32_literal(word, token_span.1).map(|v| v as i32),
false => get_i32_literal(word, token_span.1),
}
}
fn parse_atomic_pointer<'a>(
&mut self,
lexer: &mut Lexer<'a>,
@@ -3425,6 +3449,11 @@ impl Parser {
lexer,
context.as_expression(block, &mut emitter),
)?;
let uint = Some(crate::ScalarKind::Uint)
== context
.as_expression(block, &mut emitter)
.resolve_type(selector)?
.scalar_kind();
lexer.expect(Token::Paren(')'))?;
block.extend(emitter.finish(context.expressions));
lexer.expect(Token::Paren('{'))?;
@@ -3438,7 +3467,7 @@ impl Parser {
// parse a list of values
let value = loop {
// TODO: Switch statements also allow for floats, bools and unsigned integers. See https://www.w3.org/TR/WGSL/#switch-statement
let value = parse_sint_literal(lexer, 4)?;
let value = Self::parse_switch_value(lexer, uint)?;
if lexer.skip(Token::Separator(',')) {
if lexer.skip(Token::Separator(':')) {
break value;

View File

@@ -69,36 +69,6 @@ pub fn get_f32_literal(word: &str, span: Span) -> Result<f32, Error<'_>> {
parsed_val.map_err(|e| Error::BadFloat(span, e))
}
pub(super) fn parse_sint_literal<'a>(
lexer: &mut Lexer<'a>,
width: Bytes,
) -> Result<i32, Error<'a>> {
let token_span = lexer.next();
if width != 4 {
// Only 32-bit literals supported by the spec and naga for now!
return Err(Error::BadScalarWidth(token_span.1, width));
}
match token_span {
(
Token::Number {
value,
ty: NumberType::Sint,
width: token_width,
},
span,
) if token_width.unwrap_or(4) == width => get_i32_literal(value, span),
other => Err(Error::Unexpected(
other,
ExpectedToken::Number {
ty: Some(NumberType::Sint),
width: Some(width),
},
)),
}
}
pub(super) fn _parse_uint_literal<'a>(
lexer: &mut Lexer<'a>,
width: Bytes,

View File

@@ -1260,7 +1260,7 @@ pub use block::Block;
pub struct SwitchCase {
/// Value, upon which the case is considered true.
pub value: i32,
/// Body of the cae.
/// Body of the case.
pub body: Block,
/// If true, the control flow continues to the next case in the list,
/// or default.

View File

@@ -90,7 +90,7 @@ pub enum FunctionError {
InvalidIfType(Handle<crate::Expression>),
#[error("The `switch` value {0:?} is not an integer scalar")]
InvalidSwitchType(Handle<crate::Expression>),
#[error("Multiple `switch` cases for {0} are present")]
#[error("Multiple `switch` cases for {0:?} are present")]
ConflictingSwitchCase(i32),
#[error("The pointer {0:?} doesn't relate to a valid destination for a store")]
InvalidStorePointer(Handle<crate::Expression>),
@@ -375,6 +375,10 @@ impl super::Validator {
ref default,
} => {
match *context.resolve_type(selector, &self.valid_expression_set)? {
Ti::Scalar {
kind: crate::ScalarKind::Uint,
width: _,
} => {}
Ti::Scalar {
kind: crate::ScalarKind::Sint,
width: _,