diff --git a/Cargo.toml b/Cargo.toml index 92f911b71d..3e99aa6b5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ serde = { version = "1.0", features = ["derive"], optional = true } petgraph = { version ="0.5", optional = true } rose_tree = { version ="0.2", optional = true } pp-rs = { version = "0.2.1", optional = true } +hexf-parse = { version = "0.2.1", optional = true } [features] default = [] @@ -40,7 +41,7 @@ serialize = ["serde"] deserialize = ["serde"] spv-in = ["petgraph", "spirv", "rose_tree"] spv-out = ["spirv"] -wgsl-in = ["codespan-reporting"] +wgsl-in = ["codespan-reporting", "hexf-parse"] wgsl-out = [] hlsl-out = [] span = ["codespan-reporting"] diff --git a/src/front/wgsl/lexer.rs b/src/front/wgsl/lexer.rs index 39545ea732..1a3b63b2d3 100644 --- a/src/front/wgsl/lexer.rs +++ b/src/front/wgsl/lexer.rs @@ -1,4 +1,4 @@ -use super::{conv, Error, ExpectedToken, Span, Token, TokenSpan}; +use super::{conv, Error, ExpectedToken, NumberType, Span, Token, TokenSpan}; fn _consume_str<'a>(input: &'a str, what: &str) -> Option<&'a str> { if input.starts_with(what) { @@ -13,48 +13,283 @@ fn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str) { input.split_at(pos) } +/// Tries to skip a given prefix in the input string. +/// Returns whether the prefix was present and could therefore be skipped, +/// the remaining str and the number of *bytes* skipped. +pub fn try_skip_prefix<'a, 'b>(input: &'a str, prefix: &'b str) -> (bool, &'a str, usize) { + if input.starts_with(prefix) { + (true, &input[prefix.len()..], prefix.len()) + } else { + (false, input, 0) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum NLDigitState { + Nothing, + LeadingZero, + DigitBeforeDot, + OnlyDot, + DigitsThenDot, + DigitAfterDot, + Exponent, + SignAfterExponent, + DigitAfterExponent, +} + +struct NumberLexerState { + _minus: bool, + hex: bool, + leading_zeros: usize, + digit_state: NLDigitState, + uint_suffix: bool, +} + +impl NumberLexerState { + // TODO: add proper error reporting, possibly through try_into_token function returning Result + + pub fn _is_valid_number(&self) -> bool { + match *self { + Self { + _minus: false, // No negative zero for integers. + hex, + leading_zeros, + digit_state: NLDigitState::LeadingZero, + .. + } => hex || leading_zeros == 1, // No leading zeros allowed in non-hex integers, "0" is always allowed. + Self { + _minus: minus, + hex, + leading_zeros, + digit_state: NLDigitState::DigitBeforeDot, + uint_suffix, + } => { + (hex || leading_zeros == 0) // No leading zeros allowed in non-hex integers. + // In this state the number has non-zero digits, + // i.e. it is not just "0". + && (minus ^ uint_suffix) // Either a negative number, or and unsigned integer, not both. + } + _ => self.is_float(), + } + } + + pub fn is_float(&self) -> bool { + !self.uint_suffix + && (self.digit_state == NLDigitState::DigitsThenDot + || self.digit_state == NLDigitState::DigitAfterDot + || self.digit_state == NLDigitState::DigitAfterExponent) + } +} + fn consume_number(input: &str) -> (Token, &str) { - //Note: I wish this function was simpler and faster... - let mut is_first_char = true; - let mut right_after_exponent = false; + let (minus, working_substr, minus_offset) = try_skip_prefix(input, "-"); + + let (hex, working_substr, hex_offset) = try_skip_prefix(working_substr, "0x"); + + let mut state = NumberLexerState { + _minus: minus, + hex, + leading_zeros: 0, + digit_state: NLDigitState::Nothing, + uint_suffix: false, + }; let mut what = |c| { - if is_first_char { - is_first_char = false; - c == '-' || ('0'..='9').contains(&c) || c == '.' - } else if c == 'e' || c == 'E' { - right_after_exponent = true; - true - } else if right_after_exponent { - right_after_exponent = false; - ('0'..='9').contains(&c) || c == '-' - } else { - ('0'..='9').contains(&c) || c == '.' + match state { + NumberLexerState { + hex, + digit_state: NLDigitState::Nothing, + uint_suffix: false, + .. + } => match c { + '0' => { + state.digit_state = NLDigitState::LeadingZero; + state.leading_zeros += 1; + } + '1'..='9' => { + state.digit_state = NLDigitState::DigitBeforeDot; + } + 'a'..='f' | 'A'..='F' if hex => { + state.digit_state = NLDigitState::DigitBeforeDot; + } + '.' => { + state.digit_state = NLDigitState::OnlyDot; + } + _ => return false, + }, + + NumberLexerState { + hex, + digit_state: NLDigitState::LeadingZero, + uint_suffix: false, + .. + } => match c { + '0' => { + // We stay in NLDigitState::LeadingZero. + state.leading_zeros += 1; + } + '1'..='9' => { + state.digit_state = NLDigitState::DigitBeforeDot; + } + 'a'..='f' | 'A'..='F' if hex => { + state.digit_state = NLDigitState::DigitBeforeDot; + } + '.' => { + state.digit_state = NLDigitState::DigitsThenDot; + } + 'e' | 'E' if !hex => { + state.digit_state = NLDigitState::Exponent; + } + 'p' | 'P' if hex => { + state.digit_state = NLDigitState::Exponent; + } + 'u' => { + // We stay in NLDigitState::LeadingZero. + state.uint_suffix = true; + } + _ => return false, + }, + + NumberLexerState { + hex, + digit_state: NLDigitState::DigitBeforeDot, + uint_suffix: false, + .. + } => match c { + '0'..='9' => { + // We stay in NLDigitState::DigitBeforeDot. + } + 'a'..='f' | 'A'..='F' if hex => { + // We stay in NLDigitState::DigitBeforeDot. + } + '.' => { + state.digit_state = NLDigitState::DigitsThenDot; + } + 'e' | 'E' if !hex => { + state.digit_state = NLDigitState::Exponent; + } + 'p' | 'P' if hex => { + state.digit_state = NLDigitState::Exponent; + } + 'u' => { + // We stay in NLDigitState::DigitBeforeDot. + state.uint_suffix = true; + } + _ => return false, + }, + + NumberLexerState { + hex, + digit_state: NLDigitState::OnlyDot, + uint_suffix: false, + .. + } => match c { + '0'..='9' => { + state.digit_state = NLDigitState::DigitAfterDot; + } + 'a'..='f' | 'A'..='F' if hex => { + state.digit_state = NLDigitState::DigitAfterDot; + } + _ => return false, + }, + + NumberLexerState { + hex, + digit_state: NLDigitState::DigitsThenDot, + uint_suffix: false, + .. + } + | NumberLexerState { + hex, + digit_state: NLDigitState::DigitAfterDot, + uint_suffix: false, + .. + } => match c { + '0'..='9' => { + state.digit_state = NLDigitState::DigitAfterDot; + } + 'a'..='f' | 'A'..='F' if hex => { + state.digit_state = NLDigitState::DigitAfterDot; + } + 'e' | 'E' if !hex => { + state.digit_state = NLDigitState::Exponent; + } + 'p' | 'P' if hex => { + state.digit_state = NLDigitState::Exponent; + } + _ => return false, + }, + + NumberLexerState { + digit_state: NLDigitState::Exponent, + uint_suffix: false, + .. + } => match c { + '0'..='9' => { + state.digit_state = NLDigitState::DigitAfterExponent; + } + '-' | '+' => { + state.digit_state = NLDigitState::SignAfterExponent; + } + _ => return false, + }, + + NumberLexerState { + digit_state: NLDigitState::SignAfterExponent, + uint_suffix: false, + .. + } + | NumberLexerState { + digit_state: NLDigitState::DigitAfterExponent, + uint_suffix: false, + .. + } => match c { + '0'..='9' => { + state.digit_state = NLDigitState::DigitAfterExponent; + } + _ => return false, + }, + + NumberLexerState { + uint_suffix: true, .. + } => return false, // Scanning is done once we've reached a type suffix. } + + // No match branch has rejected this yet, so we are still in a number literal + true }; - let pos = input.find(|c| !what(c)).unwrap_or_else(|| input.len()); - let (value, rest) = input.split_at(pos); - - let mut rest_iter = rest.chars(); - let ty = rest_iter.next().unwrap_or(' '); - match ty { - 'u' | 'i' | 'f' => { - let width_end = rest_iter - .position(|c| !('0'..='9').contains(&c)) - .unwrap_or_else(|| rest.len() - 1); - let (width, rest) = rest[1..].split_at(width_end); - (Token::Number { value, ty, width }, rest) - } - // default to `i32` or `f32` - _ => ( - Token::Number { - value, - ty: if value.contains('.') { 'f' } else { 'i' }, - width: "", + + let pos = working_substr + .find(|c| !what(c)) + .unwrap_or_else(|| working_substr.len()); + let (value, rest) = input.split_at(pos + minus_offset + hex_offset); + + // NOTE: This code can use string slicing, + // because number literals are exclusively ASCII. + // This means all relevant characters fit into one byte + // and using string slicing (which slices UTF-8 bytes) works for us. + + // TODO: A syntax error can already be recognized here, possibly report it at this stage. + + // Return possibly knowably incorrect (given !state.is_valid_number()) token for now. + ( + Token::Number { + value: if state.uint_suffix { + &value[..value.len() - 1] + } else { + value }, - rest, - ), - } + ty: if state.uint_suffix { + NumberType::Uint + } else if state.is_float() { + NumberType::Float + } else { + NumberType::Sint + }, + width: None, + }, + rest, + ) } fn consume_token(mut input: &str, generic: bool) -> (Token<'_>, &str) { @@ -291,34 +526,6 @@ impl<'a> Lexer<'a> { } } - fn _next_float_literal(&mut self) -> Result> { - match self.next() { - (Token::Number { value, .. }, span) => { - value.parse().map_err(|e| Error::BadFloat(span, e)) - } - other => Err(Error::Unexpected(other, ExpectedToken::Float)), - } - } - - pub(super) fn next_uint_literal(&mut self) -> Result> { - match self.next() { - (Token::Number { value, .. }, span) => { - let v = value.parse(); - v.map_err(|e| Error::BadU32(span, e)) - } - other => Err(Error::Unexpected(other, ExpectedToken::Uint)), - } - } - - pub(super) fn next_sint_literal(&mut self) -> Result> { - match self.next() { - (Token::Number { value, .. }, span) => { - value.parse().map_err(|e| Error::BadI32(span, e)) - } - other => Err(Error::Unexpected(other, ExpectedToken::Sint)), - } - } - /// Parses a generic scalar type, for example ``. pub(super) fn next_scalar_generic( &mut self, @@ -409,8 +616,8 @@ fn test_tokens() { &[ Token::Number { value: "92", - ty: 'i', - width: "", + ty: NumberType::Sint, + width: None, }, Token::Word("No"), ], @@ -420,8 +627,13 @@ fn test_tokens() { &[ Token::Number { value: "2", - ty: 'u', - width: "3", + ty: NumberType::Uint, + width: None, + }, + Token::Number { + value: "3", + ty: NumberType::Sint, + width: None, }, Token::Word("o"), ], @@ -431,10 +643,10 @@ fn test_tokens() { &[ Token::Number { value: "2.4", - ty: 'f', - width: "44", + ty: NumberType::Float, + width: None, }, - Token::Word("po"), + Token::Word("f44po"), ], ); sub_test( @@ -456,8 +668,8 @@ fn test_variable_decl() { Token::Paren('('), Token::Number { value: "0", - ty: 'i', - width: "", + ty: NumberType::Sint, + width: None, }, Token::Paren(')'), Token::DoubleParen(']'), diff --git a/src/front/wgsl/mod.rs b/src/front/wgsl/mod.rs index 3b931f784d..bf97c8e0c7 100644 --- a/src/front/wgsl/mod.rs +++ b/src/front/wgsl/mod.rs @@ -4,6 +4,7 @@ mod conv; mod lexer; +mod number_literals; #[cfg(test)] mod tests; @@ -13,10 +14,16 @@ use crate::{ ensure_block_returns, Alignment, Layouter, ResolveContext, ResolveError, TypeResolution, }, span::Span as NagaSpan, - ConstantInner, FastHashMap, ScalarValue, + Bytes, ConstantInner, FastHashMap, ScalarValue, }; -use self::lexer::Lexer; +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, + }, +}; use codespan_reporting::{ diagnostic::{Diagnostic, Label}, files::{Files, SimpleFile}, @@ -25,6 +32,7 @@ use codespan_reporting::{ termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor}, }, }; +use hexf_parse::ParseHexfError; use std::{ borrow::Cow, convert::TryFrom, @@ -37,6 +45,13 @@ use thiserror::Error; type Span = ops::Range; type TokenSpan<'a> = (Token<'a>, Span); +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum NumberType { + Sint, + Uint, + Float, +} + #[derive(Copy, Clone, Debug, PartialEq)] pub enum Token<'a> { Separator(char), @@ -45,8 +60,8 @@ pub enum Token<'a> { DoubleParen(char), Number { value: &'a str, - ty: char, - width: &'a str, + ty: NumberType, + width: Option, }, String(&'a str), Word(&'a str), @@ -64,9 +79,10 @@ pub enum Token<'a> { pub enum ExpectedToken<'a> { Token(Token<'a>), Identifier, - Float, - Uint, - Sint, + Number { + ty: Option, + width: Option, + }, Constant, /// Expected: constant, parenthesized expression, identifier PrimaryExpression, @@ -88,14 +104,35 @@ pub enum ExpectedToken<'a> { StructAttribute, } +#[derive(Clone, Debug, Error)] +pub enum BadIntError { + #[error(transparent)] + ParseIntError(#[from] ParseIntError), + #[error("non-hex negative zero integer literals are not allowed")] + NegativeZero, + #[error("leading zeros for non-hex integer literals are not allowed")] + LeadingZeros, +} + +#[derive(Clone, Debug, Error)] +pub enum BadFloatError { + #[error(transparent)] + ParseFloatError(#[from] ParseFloatError), + #[error(transparent)] + ParseHexfError(#[from] ParseHexfError), +} + #[derive(Clone, Debug)] pub enum Error<'a> { Unexpected(TokenSpan<'a>, ExpectedToken<'a>), - BadU32(Span, ParseIntError), - BadI32(Span, ParseIntError), - BadFloat(Span, ParseFloatError), + BadU32(Span, BadIntError), + BadI32(Span, BadIntError), + /// A negative signed integer literal where both signed and unsigned, + /// but only non-negative literals are allowed. + NegativeInt(Span), + BadFloat(Span, BadFloatError), BadU32Constant(Span), - BadScalarWidth(Span, &'a str), + BadScalarWidth(Span, Bytes), BadAccessor(Span), BadTexture(Span), BadTypeCast { @@ -158,9 +195,26 @@ impl<'a> Error<'a> { } } ExpectedToken::Identifier => "identifier".to_string(), - ExpectedToken::Float => "floating point literal".to_string(), - ExpectedToken::Uint => "non-negative integer literal".to_string(), - ExpectedToken::Sint => "integer literal".to_string(), + ExpectedToken::Number { ty, width } => { + let literal_ty_str = match ty { + Some(NumberType::Float) => "floating-point", + Some(NumberType::Uint) => "unsigned integer", + Some(NumberType::Sint) => "signed integer", + None => "arbitrary number", + }; + if let Some(width) = width { + format!( + "{} literal of {}-bit width", + literal_ty_str, + width as u32 * 8, + ) + } else { + format!( + "{} literal of arbitrary width", + literal_ty_str, + ) + } + }, ExpectedToken::Constant => "constant".to_string(), ExpectedToken::PrimaryExpression => "expression".to_string(), ExpectedToken::AttributeSeparator => "attribute separator (',') or an end of the attribute list (']]')".to_string(), @@ -187,10 +241,10 @@ impl<'a> Error<'a> { }, Error::BadU32(ref bad_span, ref err) => ParseError { message: format!( - "expected non-negative integer literal, found `{}`", + "expected unsigned integer literal, found `{}`", &source[bad_span.clone()], ), - labels: vec![(bad_span.clone(), "expected positive integer".into())], + labels: vec![(bad_span.clone(), "expected unsigned integer".into())], notes: vec![err.to_string()], }, Error::BadI32(ref bad_span, ref err) => ParseError { @@ -198,9 +252,17 @@ impl<'a> Error<'a> { "expected integer literal, found `{}`", &source[bad_span.clone()], ), - labels: vec![(bad_span.clone(), "expected integer".into())], + labels: vec![(bad_span.clone(), "expected signed integer".into())], notes: vec![err.to_string()], }, + Error::NegativeInt(ref bad_span) => ParseError { + message: format!( + "expected non-negative integer literal, found `{}`", + &source[bad_span.clone()], + ), + labels: vec![(bad_span.clone(), "expected non-negative integer".into())], + notes: vec![], + }, Error::BadFloat(ref bad_span, ref err) => ParseError { message: format!( "expected floating-point literal, found `{}`", @@ -211,17 +273,17 @@ impl<'a> Error<'a> { }, Error::BadU32Constant(ref bad_span) => ParseError { message: format!( - "expected non-negative integer constant expression, found `{}`", + "expected unsigned integer constant expression, found `{}`", &source[bad_span.clone()], ), - labels: vec![(bad_span.clone(), "expected non-negative integer".into())], + labels: vec![(bad_span.clone(), "expected unsigned integer".into())], notes: vec![], }, Error::BadScalarWidth(ref bad_span, width) => ParseError { - message: format!("invalid width of `{}` for literal", width,), + message: format!("invalid width of `{}` bits for literal", width as u32 * 8,), labels: vec![(bad_span.clone(), "invalid width".into())], - notes: vec!["valid widths are 8, 16, 32, 64".to_string()], + notes: vec!["the only valid width is 32 for now".to_string()], }, Error::BadAccessor(ref accessor_span) => ParseError { message: format!( @@ -941,7 +1003,7 @@ impl BindingParser { match name { "location" => { lexer.expect(Token::Paren('('))?; - self.location = Some(lexer.next_uint_literal()?); + self.location = Some(parse_non_negative_sint_literal(lexer, 4)?); lexer.expect(Token::Paren(')'))?; } "builtin" => { @@ -1103,36 +1165,34 @@ impl Parser { fn get_constant_inner<'a>( word: &'a str, - ty: char, - width: &'a str, - token: TokenSpan<'a>, + ty: NumberType, + width: Option, + token_span: TokenSpan<'a>, ) -> Result> { - let span = token.1; + let span = token_span.1; + + if let Some(width) = width { + if width != 4 { + // Only 32-bit literals supported by the spec and naga for now! + return Err(Error::BadScalarWidth(span, width)); + } + } + let value = match ty { - 'i' => word - .parse() - .map(crate::ScalarValue::Sint) - .map_err(|e| Error::BadI32(span.clone(), e))?, - 'u' => word - .parse() - .map(crate::ScalarValue::Uint) - .map_err(|e| Error::BadU32(span.clone(), e))?, - 'f' => word - .parse() - .map(crate::ScalarValue::Float) - .map_err(|e| Error::BadFloat(span.clone(), e))?, - _ => unreachable!(), + NumberType::Sint => { + get_i32_literal(word, span).map(|val| crate::ScalarValue::Sint(val as i64))? + } + NumberType::Uint => { + get_u32_literal(word, span).map(|val| crate::ScalarValue::Uint(val as u64))? + } + NumberType::Float => { + get_f32_literal(word, span).map(|val| crate::ScalarValue::Float(val as f64))? + } }; + Ok(crate::ConstantInner::Scalar { value, - width: if width.is_empty() { - 4 - } else { - match width.parse::() { - Ok(bits) if (bits % 8) == 0 => Ok(bits / 8), - _ => Err(Error::BadScalarWidth(span, width)), - }? - }, + width: width.unwrap_or(4), }) } @@ -2424,8 +2484,9 @@ impl Parser { match word { "size" => { lexer.expect(Token::Paren('('))?; - let (value, span) = - lexer.capture_span(Lexer::next_uint_literal)?; + let (value, span) = lexer.capture_span(|lexer| { + parse_non_negative_sint_literal(lexer, 4) + })?; lexer.expect(Token::Paren(')'))?; size = Some( NonZeroU32::new(value) @@ -2434,8 +2495,9 @@ impl Parser { } "align" => { lexer.expect(Token::Paren('('))?; - let (value, span) = - lexer.capture_span(Lexer::next_uint_literal)?; + let (value, span) = lexer.capture_span(|lexer| { + parse_non_negative_sint_literal(lexer, 4) + })?; lexer.expect(Token::Paren(')'))?; align = Some( NonZeroU32::new(value) @@ -2841,7 +2903,8 @@ impl Parser { match lexer.next() { (Token::Word("stride"), _) => { lexer.expect(Token::Paren('('))?; - let (stride, span) = lexer.capture_span(Lexer::next_uint_literal)?; + let (stride, span) = lexer + .capture_span(|lexer| parse_non_negative_sint_literal(lexer, 4))?; attribute.stride = Some(NonZeroU32::new(stride).ok_or(Error::ZeroStride(span))?); lexer.expect(Token::Paren(')'))?; @@ -3212,7 +3275,8 @@ impl Parser { (Token::Word("case"), _) => { // parse a list of values let value = loop { - let value = lexer.next_sint_literal()?; + // 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)?; if lexer.skip(Token::Separator(',')) { if lexer.skip(Token::Separator(':')) { break value; @@ -3642,7 +3706,7 @@ impl Parser { match lexer.next_ident_with_span()? { ("binding", _) => { lexer.expect(Token::Paren('('))?; - bind_index = Some(lexer.next_uint_literal()?); + bind_index = Some(parse_non_negative_sint_literal(lexer, 4)?); lexer.expect(Token::Paren(')'))?; } ("block", _) => { @@ -3650,7 +3714,7 @@ impl Parser { } ("group", _) => { lexer.expect(Token::Paren('('))?; - bind_group = Some(lexer.next_uint_literal()?); + bind_group = Some(parse_non_negative_sint_literal(lexer, 4)?); lexer.expect(Token::Paren(')'))?; } ("stage", _) => { @@ -3662,7 +3726,7 @@ impl Parser { ("workgroup_size", _) => { lexer.expect(Token::Paren('('))?; for (i, size) in workgroup_size.iter_mut().enumerate() { - *size = lexer.next_uint_literal()?; + *size = parse_generic_non_negative_int_literal(lexer, 4)?; match lexer.next() { (Token::Paren(')'), _) => break, (Token::Separator(','), _) if i != 2 => (), diff --git a/src/front/wgsl/number_literals.rs b/src/front/wgsl/number_literals.rs new file mode 100644 index 0000000000..2f61224142 --- /dev/null +++ b/src/front/wgsl/number_literals.rs @@ -0,0 +1,239 @@ +use std::convert::TryFrom; + +use hexf_parse::parse_hexf32; + +use crate::Bytes; + +use super::{ + lexer::{try_skip_prefix, Lexer}, + BadFloatError, BadIntError, Error, ExpectedToken, NumberType, Span, Token, +}; + +fn check_int_literal(word_without_minus: &str, minus: bool, hex: bool) -> Result<(), BadIntError> { + let leading_zeros = word_without_minus + .bytes() + .take_while(|&b| b == b'0') + .count(); + + if word_without_minus == "0" && minus { + Err(BadIntError::NegativeZero) + } else if word_without_minus != "0" && !hex && leading_zeros != 0 { + Err(BadIntError::LeadingZeros) + } else { + Ok(()) + } +} + +pub fn get_i32_literal(word: &str, span: Span) -> Result> { + let (minus, word_without_minus, _) = try_skip_prefix(word, "-"); + let (hex, word_without_minus_and_0x, _) = try_skip_prefix(word_without_minus, "0x"); + + check_int_literal(word_without_minus, minus, hex) + .map_err(|e| Error::BadI32(span.clone(), e))?; + + let parsed_val = match (hex, minus) { + (true, true) => i32::from_str_radix(&format!("-{}", word_without_minus_and_0x), 16), + (true, false) => i32::from_str_radix(word_without_minus_and_0x, 16), + (false, _) => word.parse(), + }; + + parsed_val.map_err(|e| Error::BadI32(span, e.into())) +} + +pub fn get_u32_literal(word: &str, span: Span) -> Result> { + let (minus, word_without_minus, _) = try_skip_prefix(word, "-"); + let (hex, word_without_minus_and_0x, _) = try_skip_prefix(word_without_minus, "0x"); + + check_int_literal(word_without_minus, minus, hex) + .map_err(|e| Error::BadU32(span.clone(), e))?; + + // We need to add a minus here as well, since the lexer also accepts syntactically incorrect negative uints + let parsed_val = match (hex, minus) { + (true, true) => u32::from_str_radix(&format!("-{}", word_without_minus_and_0x), 16), + (true, false) => u32::from_str_radix(word_without_minus_and_0x, 16), + (false, _) => word.parse(), + }; + + parsed_val.map_err(|e| Error::BadU32(span, e.into())) +} + +pub fn get_f32_literal(word: &str, span: Span) -> Result> { + let hex = word.starts_with("0x") || word.starts_with("-0x"); + + let parsed_val = if hex { + parse_hexf32(word, false).map_err(BadFloatError::ParseHexfError) + } else { + word.parse::().map_err(BadFloatError::ParseFloatError) + }; + + parsed_val.map_err(|e| Error::BadFloat(span, e)) +} + +pub(super) fn parse_sint_literal<'a>( + lexer: &mut Lexer<'a>, + width: Bytes, +) -> Result> { + 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, +) -> Result> { + 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::Uint, + width: token_width, + }, + span, + ) if token_width.unwrap_or(4) == width => get_u32_literal(value, span), + other => Err(Error::Unexpected( + other, + ExpectedToken::Number { + ty: Some(NumberType::Uint), + width: Some(width), + }, + )), + } +} + +/// Parse a non-negative signed integer literal. +/// This is for attributes like `size`, `location` and others. +pub(super) fn parse_non_negative_sint_literal<'a>( + lexer: &mut Lexer<'a>, + width: Bytes, +) -> Result> { + 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 => { + let i32_val = get_i32_literal(value, span.clone())?; + u32::try_from(i32_val).map_err(|_| Error::NegativeInt(span)) + } + other => Err(Error::Unexpected( + other, + ExpectedToken::Number { + ty: Some(NumberType::Sint), + width: Some(width), + }, + )), + } +} + +/// Parse a non-negative integer literal that may be either signed or unsigned. +/// This is for the `workgroup_size` attribute and array lengths. +/// Note: these values should be no larger than [`i32::MAX`], but this is not checked here. +pub(super) fn parse_generic_non_negative_int_literal<'a>( + lexer: &mut Lexer<'a>, + width: Bytes, +) -> Result> { + 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 => { + let i32_val = get_i32_literal(value, span.clone())?; + u32::try_from(i32_val).map_err(|_| Error::NegativeInt(span)) + } + ( + Token::Number { + value, + ty: NumberType::Uint, + width: token_width, + }, + span, + ) if token_width.unwrap_or(4) == width => get_u32_literal(value, span), + other => Err(Error::Unexpected( + other, + ExpectedToken::Number { + ty: Some(NumberType::Sint), + width: Some(width), + }, + )), + } +} + +pub(super) fn _parse_float_literal<'a>( + lexer: &mut Lexer<'a>, + width: Bytes, +) -> Result> { + 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::Float, + width: token_width, + }, + span, + ) if token_width.unwrap_or(4) == width => get_f32_literal(value, span), + other => Err(Error::Unexpected( + other, + ExpectedToken::Number { + ty: Some(NumberType::Float), + width: Some(width), + }, + )), + } +} diff --git a/src/front/wgsl/tests.rs b/src/front/wgsl/tests.rs index d22310945b..432b39f466 100644 --- a/src/front/wgsl/tests.rs +++ b/src/front/wgsl/tests.rs @@ -14,6 +14,76 @@ fn parse_comment() { .unwrap(); } +// Regexes for the literals are taken from the working draft at +// https://www.w3.org/TR/2021/WD-WGSL-20210806/#literals + +#[test] +fn parse_decimal_floats() { + // /^(-?[0-9]*\.[0-9]+|-?[0-9]+\.[0-9]*)((e|E)(\+|-)?[0-9]+)?$/ + parse_str("let a : f32 = -1.;").unwrap(); + parse_str("let a : f32 = -.1;").unwrap(); + parse_str("let a : f32 = 42.1234;").unwrap(); + parse_str("let a : f32 = -1.E3;").unwrap(); + parse_str("let a : f32 = -.1e-5;").unwrap(); + parse_str("let a : f32 = 2.3e+55;").unwrap(); + + assert!(parse_str("let a : f32 = 42.1234f;").is_err()); + assert!(parse_str("let a : f32 = 42.1234f32;").is_err()); +} + +#[test] +fn parse_hex_floats() { + // /^-?0x([0-9a-fA-F]*\.?[0-9a-fA-F]+|[0-9a-fA-F]+\.[0-9a-fA-F]*)(p|P)(\+|-)?[0-9]+$/ + parse_str("let a : f32 = -0xa.p1;").unwrap(); + parse_str("let a : f32 = -0x.fp9;").unwrap(); + parse_str("let a : f32 = 0x2a.4D2P4;").unwrap(); + parse_str("let a : f32 = -0x.1p-5;").unwrap(); + parse_str("let a : f32 = 0xC.8p+55;").unwrap(); + parse_str("let a : f32 = 0x1p1;").unwrap(); + + assert!(parse_str("let a : f32 = 0x1p1f;").is_err()); + assert!(parse_str("let a : f32 = 0x1p1f32;").is_err()); +} + +#[test] +fn parse_decimal_ints() { + // i32 /^-?0x[0-9a-fA-F]+|0|-?[1-9][0-9]*$/ + parse_str("let a : i32 = 0;").unwrap(); + parse_str("let a : i32 = 1092;").unwrap(); + parse_str("let a : i32 = -9923;").unwrap(); + + assert!(parse_str("let a : i32 = -0;").is_err()); + assert!(parse_str("let a : i32 = 01;").is_err()); + assert!(parse_str("let a : i32 = 1.0;").is_err()); + assert!(parse_str("let a : i32 = 1i;").is_err()); + assert!(parse_str("let a : i32 = 1i32;").is_err()); + + // u32 /^0x[0-9a-fA-F]+u|0u|[1-9][0-9]*u$/ + parse_str("let a : u32 = 0u;").unwrap(); + parse_str("let a : u32 = 1092u;").unwrap(); + + assert!(parse_str("let a : u32 = -0u;").is_err()); + assert!(parse_str("let a : u32 = 01u;").is_err()); + assert!(parse_str("let a : u32 = 1.0u;").is_err()); + assert!(parse_str("let a : u32 = 1u32;").is_err()); +} + +#[test] +fn parse_hex_ints() { + // i32 /^-?0x[0-9a-fA-F]+|0|-?[1-9][0-9]*$/ + parse_str("let a : i32 = -0x0;").unwrap(); + parse_str("let a : i32 = 0x2a4D2;").unwrap(); + + assert!(parse_str("let a : i32 = 0x2a4D2i;").is_err()); + assert!(parse_str("let a : i32 = 0x2a4D2i32;").is_err()); + + // u32 /^0x[0-9a-fA-F]+u|0u|[1-9][0-9]*u$/ + parse_str("let a : u32 = 0x0u;").unwrap(); + parse_str("let a : u32 = 0x2a4D2u;").unwrap(); + + assert!(parse_str("let a : u32 = 0x2a4D2u32;").is_err()); +} + #[test] fn parse_types() { parse_str("let a : i32 = 2;").unwrap(); @@ -32,7 +102,7 @@ fn parse_type_inference() { fn foo() { let a = 2u; let b: u32 = a; - var x = 3f32; + var x = 3.; var y = vec2(1, 2); }", ) diff --git a/tests/in/interpolate.wgsl b/tests/in/interpolate.wgsl index f95c0b6ca9..ba134c9437 100644 --- a/tests/in/interpolate.wgsl +++ b/tests/in/interpolate.wgsl @@ -16,7 +16,7 @@ fn main() -> FragmentInput { var out: FragmentInput; out.position = vec4(2.0, 4.0, 5.0, 6.0); - out.flat = 8u32; + out.flat = 8u; out.linear = 27.0; out.linear_centroid = vec2(64.0, 125.0); out.linear_sample = vec3(216.0, 343.0, 512.0); diff --git a/tests/out/glsl/boids.main.Compute.glsl b/tests/out/glsl/boids.main.Compute.glsl index 7a925d554e..e4620e6626 100644 --- a/tests/out/glsl/boids.main.Compute.glsl +++ b/tests/out/glsl/boids.main.Compute.glsl @@ -126,7 +126,7 @@ void main() { vVel = (((_e107 + (_e108 * _e110)) + (_e113 * _e115)) + (_e118 * _e120)); vec2 _e123 = vVel; vec2 _e125 = vVel; - vVel = (normalize(_e123) * clamp(length(_e125), 0.0, 0.1)); + vVel = (normalize(_e123) * clamp(length(_e125), 0.0, 0.10000000149011612)); vec2 _e131 = vPos; vec2 _e132 = vVel; float _e134 = _group_0_binding_0.deltaT; diff --git a/tests/out/glsl/operators.main.Compute.glsl b/tests/out/glsl/operators.main.Compute.glsl index 1c1cf2bd1b..6c679229a2 100644 --- a/tests/out/glsl/operators.main.Compute.glsl +++ b/tests/out/glsl/operators.main.Compute.glsl @@ -11,7 +11,7 @@ vec4 builtins() { vec4 s2_ = (true ? vec4(1.0, 1.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 0.0)); vec4 s3_ = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), bvec4(false, false, false, false)); vec4 m1_ = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), vec4(0.5, 0.5, 0.5, 0.5)); - vec4 m2_ = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), 0.1); + vec4 m2_ = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), 0.10000000149011612); float b1_ = intBitsToFloat(ivec4(1, 1, 1, 1).x); vec4 b2_ = intBitsToFloat(ivec4(1, 1, 1, 1)); return (((((vec4(ivec4(s1_)) + s2_) + m1_) + m2_) + vec4(b1_)) + b2_); diff --git a/tests/out/glsl/quad.main.Vertex.glsl b/tests/out/glsl/quad.main.Vertex.glsl index 63a4472837..cda43f5086 100644 --- a/tests/out/glsl/quad.main.Vertex.glsl +++ b/tests/out/glsl/quad.main.Vertex.glsl @@ -15,7 +15,7 @@ smooth out vec2 _vs2fs_location0; void main() { vec2 pos = _p2vs_location0; vec2 uv = _p2vs_location1; - VertexOutput _tmp_return = VertexOutput(uv, vec4((1.2 * pos), 0.0, 1.0)); + VertexOutput _tmp_return = VertexOutput(uv, vec4((1.2000000476837158 * pos), 0.0, 1.0)); _vs2fs_location0 = _tmp_return.uv; gl_Position = _tmp_return.position; return; diff --git a/tests/out/glsl/shadow.fs_main.Fragment.glsl b/tests/out/glsl/shadow.fs_main.Fragment.glsl index e3502e2bcc..7e5bf36538 100644 --- a/tests/out/glsl/shadow.fs_main.Fragment.glsl +++ b/tests/out/glsl/shadow.fs_main.Fragment.glsl @@ -36,7 +36,7 @@ float fetch_shadow(uint light_id, vec4 homogeneous_coords) { void main() { vec3 raw_normal = _vs2fs_location0; vec4 position = _vs2fs_location1; - vec3 color = vec3(0.05, 0.05, 0.05); + vec3 color = vec3(0.05000000074505806, 0.05000000074505806, 0.05000000074505806); uint i = 0u; vec3 normal = normalize(raw_normal); bool loop_init = true; diff --git a/tests/out/hlsl/boids.hlsl b/tests/out/hlsl/boids.hlsl index 9288e5c0cd..02744cc40c 100644 --- a/tests/out/hlsl/boids.hlsl +++ b/tests/out/hlsl/boids.hlsl @@ -118,7 +118,7 @@ void main(uint3 global_invocation_id : SV_DispatchThreadID) vVel = (((_expr107 + (_expr108 * _expr110)) + (_expr113 * _expr115)) + (_expr118 * _expr120)); float2 _expr123 = vVel; float2 _expr125 = vVel; - vVel = (normalize(_expr123) * clamp(length(_expr125), 0.0, 0.1)); + vVel = (normalize(_expr123) * clamp(length(_expr125), 0.0, 0.10000000149011612)); float2 _expr131 = vPos; float2 _expr132 = vVel; float _expr134 = params.deltaT; diff --git a/tests/out/hlsl/image.hlsl b/tests/out/hlsl/image.hlsl index d2d46cfda2..65f49a1a63 100644 --- a/tests/out/hlsl/image.hlsl +++ b/tests/out/hlsl/image.hlsl @@ -200,8 +200,8 @@ float4 sample1() : SV_Target0 float2 tc = float2(0.5.xx); float4 s2d = image_2d.Sample(sampler_reg, tc); float4 s2d_offset = image_2d.Sample(sampler_reg, tc, int2(3, 1)); - float4 s2d_level = image_2d.SampleLevel(sampler_reg, tc, 2.3); - float4 s2d_level_offset = image_2d.SampleLevel(sampler_reg, tc, 2.3, int2(3, 1)); + float4 s2d_level = image_2d.SampleLevel(sampler_reg, tc, 2.299999952316284); + float4 s2d_level_offset = image_2d.SampleLevel(sampler_reg, tc, 2.299999952316284, int2(3, 1)); return (((s2d + s2d_offset) + s2d_level) + s2d_level_offset); } diff --git a/tests/out/hlsl/operators.hlsl b/tests/out/hlsl/operators.hlsl index 98a0da2931..de36774a2f 100644 --- a/tests/out/hlsl/operators.hlsl +++ b/tests/out/hlsl/operators.hlsl @@ -9,7 +9,7 @@ float4 builtins() float4 s2_ = (true ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 0.0)); float4 s3_ = (bool4(false, false, false, false) ? float4(0.0, 0.0, 0.0, 0.0) : float4(1.0, 1.0, 1.0, 1.0)); float4 m1_ = lerp(float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0), float4(0.5, 0.5, 0.5, 0.5)); - float4 m2_ = lerp(float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0), 0.1); + float4 m2_ = lerp(float4(0.0, 0.0, 0.0, 0.0), float4(1.0, 1.0, 1.0, 1.0), 0.10000000149011612); float b1_ = float(int4(1, 1, 1, 1).x); float4 b2_ = float4(int4(1, 1, 1, 1)); return (((((float4(int4(s1_.xxxx)) + s2_) + m1_) + m2_) + float4(b1_.xxxx)) + b2_); diff --git a/tests/out/hlsl/quad.hlsl b/tests/out/hlsl/quad.hlsl index 2aba7768e9..c12c5184ce 100644 --- a/tests/out/hlsl/quad.hlsl +++ b/tests/out/hlsl/quad.hlsl @@ -1,4 +1,4 @@ -static const float c_scale = 1.2; +static const float c_scale = 1.2000000476837158; struct VertexOutput { linear float2 uv : LOC0; diff --git a/tests/out/hlsl/shadow.hlsl b/tests/out/hlsl/shadow.hlsl index 85cdbf88f1..0f878d6a8e 100644 --- a/tests/out/hlsl/shadow.hlsl +++ b/tests/out/hlsl/shadow.hlsl @@ -1,4 +1,4 @@ -static const float3 c_ambient = float3(0.05, 0.05, 0.05); +static const float3 c_ambient = float3(0.05000000074505806, 0.05000000074505806, 0.05000000074505806); static const uint c_max_lights = 10; struct Globals { @@ -36,7 +36,7 @@ float4 fs_main(FragmentInput_fs_main fragmentinput_fs_main) : SV_Target0 { float3 raw_normal = fragmentinput_fs_main.raw_normal1; float4 position = fragmentinput_fs_main.position1; - float3 color = float3(0.05, 0.05, 0.05); + float3 color = float3(0.05000000074505806, 0.05000000074505806, 0.05000000074505806); uint i = 0u; float3 normal = normalize(raw_normal); diff --git a/tests/out/msl/boids.msl b/tests/out/msl/boids.msl index ef9d6d7107..53edfad1f3 100644 --- a/tests/out/msl/boids.msl +++ b/tests/out/msl/boids.msl @@ -130,7 +130,7 @@ kernel void main1( vVel = ((_e107 + (_e108 * _e110)) + (_e113 * _e115)) + (_e118 * _e120); metal::float2 _e123 = vVel; metal::float2 _e125 = vVel; - vVel = metal::normalize(_e123) * metal::clamp(metal::length(_e125), 0.0, 0.1); + vVel = metal::normalize(_e123) * metal::clamp(metal::length(_e125), 0.0, 0.10000000149011612); metal::float2 _e131 = vPos; metal::float2 _e132 = vVel; float _e134 = params.deltaT; diff --git a/tests/out/msl/image.msl b/tests/out/msl/image.msl index 3a615ef12f..2fbe17aa8f 100644 --- a/tests/out/msl/image.msl +++ b/tests/out/msl/image.msl @@ -73,8 +73,8 @@ fragment sampleOutput sample( metal::float2 tc = metal::float2(0.5); metal::float4 s2d = image_2d.sample(sampler_reg, tc); metal::float4 s2d_offset = image_2d.sample(sampler_reg, tc, const_type7_); - metal::float4 s2d_level = image_2d.sample(sampler_reg, tc, metal::level(2.3)); - metal::float4 s2d_level_offset = image_2d.sample(sampler_reg, tc, metal::level(2.3), const_type7_); + metal::float4 s2d_level = image_2d.sample(sampler_reg, tc, metal::level(2.299999952316284)); + metal::float4 s2d_level_offset = image_2d.sample(sampler_reg, tc, metal::level(2.299999952316284), const_type7_); return sampleOutput { ((s2d + s2d_offset) + s2d_level) + s2d_level_offset }; } diff --git a/tests/out/msl/operators.msl b/tests/out/msl/operators.msl index 105da6b90f..fd03a429b6 100644 --- a/tests/out/msl/operators.msl +++ b/tests/out/msl/operators.msl @@ -13,7 +13,7 @@ metal::float4 builtins( metal::float4 s2_ = true ? v_f32_one : v_f32_zero; metal::float4 s3_ = metal::select(v_f32_one, v_f32_zero, metal::bool4(false, false, false, false)); metal::float4 m1_ = metal::mix(v_f32_zero, v_f32_one, v_f32_half); - metal::float4 m2_ = metal::mix(v_f32_zero, v_f32_one, 0.1); + metal::float4 m2_ = metal::mix(v_f32_zero, v_f32_one, 0.10000000149011612); float b1_ = as_type(v_i32_one.x); metal::float4 b2_ = as_type(v_i32_one); return ((((static_cast(metal::int4(s1_)) + s2_) + m1_) + m2_) + metal::float4(b1_)) + b2_; diff --git a/tests/out/msl/quad.msl b/tests/out/msl/quad.msl index 9811ae517b..bb7e971bef 100644 --- a/tests/out/msl/quad.msl +++ b/tests/out/msl/quad.msl @@ -2,7 +2,7 @@ #include #include -constexpr constant float c_scale = 1.2; +constexpr constant float c_scale = 1.2000000476837158; struct VertexOutput { metal::float2 uv; metal::float4 position; diff --git a/tests/out/msl/shadow.msl b/tests/out/msl/shadow.msl index 60a9a023dd..c098c0142a 100644 --- a/tests/out/msl/shadow.msl +++ b/tests/out/msl/shadow.msl @@ -19,7 +19,7 @@ typedef Light type3[1]; struct Lights { type3 data; }; -constant metal::float3 c_ambient = {0.05, 0.05, 0.05}; +constant metal::float3 c_ambient = {0.05000000074505806, 0.05000000074505806, 0.05000000074505806}; float fetch_shadow( metal::uint light_id, diff --git a/tests/out/wgsl/boids.wgsl b/tests/out/wgsl/boids.wgsl index 283169b58f..5c510d2bf5 100644 --- a/tests/out/wgsl/boids.wgsl +++ b/tests/out/wgsl/boids.wgsl @@ -124,7 +124,7 @@ fn main([[builtin(global_invocation_id)]] global_invocation_id: vec3) { vVel = (((_e107 + (_e108 * _e110)) + (_e113 * _e115)) + (_e118 * _e120)); let _e123: vec2 = vVel; let _e125: vec2 = vVel; - vVel = (normalize(_e123) * clamp(length(_e125), 0.0, 0.1)); + vVel = (normalize(_e123) * clamp(length(_e125), 0.0, 0.10000000149011612)); let _e131: vec2 = vPos; let _e132: vec2 = vVel; let _e134: f32 = params.deltaT; diff --git a/tests/out/wgsl/image.wgsl b/tests/out/wgsl/image.wgsl index 46f4ee05ed..8fc336f7db 100644 --- a/tests/out/wgsl/image.wgsl +++ b/tests/out/wgsl/image.wgsl @@ -74,8 +74,8 @@ fn sample() -> [[location(0)]] vec4 { let tc: vec2 = vec2(0.5); let s2d: vec4 = textureSample(image_2d, sampler_reg, tc); let s2d_offset: vec4 = textureSample(image_2d, sampler_reg, tc, vec2(3, 1)); - let s2d_level: vec4 = textureSampleLevel(image_2d, sampler_reg, tc, 2.3); - let s2d_level_offset: vec4 = textureSampleLevel(image_2d, sampler_reg, tc, 2.3, vec2(3, 1)); + let s2d_level: vec4 = textureSampleLevel(image_2d, sampler_reg, tc, 2.299999952316284); + let s2d_level_offset: vec4 = textureSampleLevel(image_2d, sampler_reg, tc, 2.299999952316284, vec2(3, 1)); return (((s2d + s2d_offset) + s2d_level) + s2d_level_offset); } diff --git a/tests/out/wgsl/operators.wgsl b/tests/out/wgsl/operators.wgsl index 569804ed10..a4cf8bdd5f 100644 --- a/tests/out/wgsl/operators.wgsl +++ b/tests/out/wgsl/operators.wgsl @@ -7,7 +7,7 @@ fn builtins() -> vec4 { let s2_: vec4 = select(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), true); let s3_: vec4 = select(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 0.0, 0.0), vec4(false, false, false, false)); let m1_: vec4 = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), vec4(0.5, 0.5, 0.5, 0.5)); - let m2_: vec4 = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), 0.1); + let m2_: vec4 = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0), 0.10000000149011612); let b1_: f32 = bitcast(vec4(1, 1, 1, 1).x); let b2_: vec4 = vec4(vec4(1, 1, 1, 1)); return (((((vec4(vec4(s1_)) + s2_) + m1_) + m2_) + vec4(b1_)) + b2_); diff --git a/tests/out/wgsl/quad.wgsl b/tests/out/wgsl/quad.wgsl index 0b2a0c1d89..9fccc34b49 100644 --- a/tests/out/wgsl/quad.wgsl +++ b/tests/out/wgsl/quad.wgsl @@ -3,7 +3,7 @@ struct VertexOutput { [[builtin(position)]] position: vec4; }; -let c_scale: f32 = 1.2; +let c_scale: f32 = 1.2000000476837158; [[group(0), binding(0)]] var u_texture: texture_2d; diff --git a/tests/out/wgsl/shadow.wgsl b/tests/out/wgsl/shadow.wgsl index a0b5fbd9a5..49c70fa9be 100644 --- a/tests/out/wgsl/shadow.wgsl +++ b/tests/out/wgsl/shadow.wgsl @@ -14,7 +14,7 @@ struct Lights { data: [[stride(96)]] array; }; -let c_ambient: vec3 = vec3(0.05, 0.05, 0.05); +let c_ambient: vec3 = vec3(0.05000000074505806, 0.05000000074505806, 0.05000000074505806); let c_max_lights: u32 = 10u; [[group(0), binding(0)]] @@ -38,7 +38,7 @@ fn fetch_shadow(light_id: u32, homogeneous_coords: vec4) -> f32 { [[stage(fragment)]] fn fs_main([[location(0)]] raw_normal: vec3, [[location(1)]] position: vec4) -> [[location(0)]] vec4 { - var color: vec3 = vec3(0.05, 0.05, 0.05); + var color: vec3 = vec3(0.05000000074505806, 0.05000000074505806, 0.05000000074505806); var i: u32 = 0u; let normal: vec3 = normalize(raw_normal); diff --git a/tests/wgsl-errors.rs b/tests/wgsl-errors.rs index a99cd96aae..0556cb8245 100644 --- a/tests/wgsl-errors.rs +++ b/tests/wgsl-errors.rs @@ -49,29 +49,11 @@ fn invalid_integer() { fn invalid_float() { check( "let scale: f32 = 1.1.;", - r###"error: expected floating-point literal, found `1.1.` - ┌─ wgsl:1:18 + r###"error: expected ';', found '.' + ┌─ wgsl:1:21 │ 1 │ let scale: f32 = 1.1.; - │ ^^^^ expected floating-point literal - │ - = note: invalid float literal - -"###, - ); -} - -#[test] -fn invalid_scalar_width() { - check( - "let scale: f32 = 1.1f1000;", - r###"error: invalid width of `1000` for literal - ┌─ wgsl:1:18 - │ -1 │ let scale: f32 = 1.1f1000; - │ ^^^^^^^^ invalid width - │ - = note: valid widths are 8, 16, 32, 64 + │ ^ expected ';' "###, ); @@ -118,11 +100,11 @@ fn negative_index() { return a[-1]; } "#, - r#"error: expected non-negative integer constant expression, found `-1` + r#"error: expected unsigned integer constant expression, found `-1` ┌─ wgsl:4:26 │ 4 │ return a[-1]; - │ ^^ expected non-negative integer + │ ^^ expected unsigned integer "#, );