diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f0dba102 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,48 @@ +# Shared editor settings. Language formatters remain authoritative for syntax. +# Modern K&R: +# - Opening braces stay on the same line. +# - Block bodies stay expanded and readable. +# - Keep exactly one blank line between function and method definitions. + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +tab_width = 4 +trim_trailing_whitespace = true + +# Soft guide only. Language formatters remain responsible for wrapping. +max_line_length = 100 + +[*.rs] +# Rust source layout. +indent_style = space +indent_size = 4 +tab_width = 4 + +# Keep function/method definitions visually separated by one blank line. +blank_lines_around_function = 1 +blank_lines_around_method = 1 + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +# Preserve Markdown's two-space hard line breaks. +trim_trailing_whitespace = false + +[{Makefile,makefile,GNUmakefile,*.mk}] +indent_style = tab +indent_size = tab + +[tests/fixtures/**] +# Fixture bytes may deliberately exercise whitespace and missing final newlines. +trim_trailing_whitespace = false +insert_final_newline = false +charset = unset +end_of_line = unset +max_line_length = off \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 31d9647c..e7277b55 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ *.wave linguist-language=Wave tests/** linguist-detectable=false +# Match rustfmt's Unix newline style even with core.autocrlf enabled on Windows. +*.rs text eol=lf # AST snapshots assert UTF-8 byte offsets, independently of checkout defaults. tests/fixtures/ast/* text eol=lf diff --git a/.gitignore b/.gitignore index 26af3e59..ef57148a 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,30 @@ cmake-build-debug/ cmake-build-release/ .DS_Store + +# AI assistant instructions, configuration, and local state (at any depth) +AGENTS.md +AGENTS.override.md +CLAUDE.md +CLAUDE.local.md +GEMINI.md +.codex +.agents +.claude +.cursor +.cursorrules +.cursorignore +.windsurf +.windsurfrules +.continue +.clinerules +.roo +.roomodes +.aider.conf.yml +.aiderignore +.aider.chat.history.md +.aider.input.history +.github/copilot-instructions.md +.github/instructions/ +.github/prompts/ +.github/agents/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a56fbe2b..965d2598 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ [[package]] name = "ir" version = "0.1.0" -source = "git+https://github.com/wavefnd/Whale.git?branch=master#4f45fdb53d07882932cc251856316206a3bb3947" +source = "git+https://github.com/wavefnd/Whale.git?branch=master#020d2a4cf312123518ec75cc865eea1d257c5689" dependencies = [ "whale-target", ] @@ -238,4 +238,4 @@ dependencies = [ [[package]] name = "whale-target" version = "0.1.0" -source = "git+https://github.com/wavefnd/Whale.git?branch=master#4f45fdb53d07882932cc251856316206a3bb3947" +source = "git+https://github.com/wavefnd/Whale.git?branch=master#020d2a4cf312123518ec75cc865eea1d257c5689" diff --git a/README.md b/README.md index 52250bd7..12eb175a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@
- Wave programming language logo + Wave programming language logo -

Wave

+---

A general-purpose programming language built as a modern evolution of C.

Native compilation and direct control, rebuilt for modern software and tooling.

@@ -97,14 +97,13 @@ which controls the C ABI boundary. `main` is always a private entry point, so Linux and macOS: ```shell -curl -fsSL https://wave-lang.dev/install.sh | bash -s -- latest +curl -fsSL https://wave-lang.dev/install.sh | bash ``` Windows PowerShell: ```powershell -irm https://wave-lang.dev/install.ps1 -OutFile install.ps1 -powershell -ExecutionPolicy Bypass -File .\install.ps1 -Latest +irm https://wave-lang.dev/install.ps1 -OutFile install.ps1; powershell -ExecutionPolicy Bypass -File .\install.ps1 ``` See the [installation guide](https://wave-lang.dev/docs/getting-started/install) for platform requirements and release selection. diff --git a/build.rs b/build.rs index 8d07ac06..890ea9e3 100644 --- a/build.rs +++ b/build.rs @@ -3,11 +3,9 @@ use std::{env, path::Path, process::Command}; fn git(args: &[&str]) -> Option { let result = Command::new("git").args(args).output().ok()?; - result - .status - .success() - .then(|| String::from_utf8_lossy(&result.stdout).trim().to_owned()) + result.status.success().then(|| String::from_utf8_lossy(&result.stdout).trim().to_owned()) } + fn main() { println!("cargo:rerun-if-env-changed=WAVE_STD_REVISION"); // Track both detached HEAD and branch ref changes, including worktrees. @@ -32,9 +30,7 @@ fn main() { // Do not accidentally record an enclosing, unrelated repository. let root = git(&["rev-parse", "--show-toplevel"])?; if Path::new(&root).canonicalize().ok()? - != Path::new(&env::var("CARGO_MANIFEST_DIR").ok()?) - .canonicalize() - .ok()? + != Path::new(&env::var("CARGO_MANIFEST_DIR").ok()?).canonicalize().ok()? { return None; } diff --git a/front/error/src/error.rs b/front/error/src/error.rs index 4304f182..2f46e6f8 100644 --- a/front/error/src/error.rs +++ b/front/error/src/error.rs @@ -285,12 +285,8 @@ impl WaveError { ("help", Value::optional_string(self.help.as_deref())), ("suggestions", Value::strings(&self.suggestions)), ]); - wson::dumps( - &Value::object([("error", error)]), - wson::Format::Json, - false, - ) - .expect("diagnostic schema contains only shallow JSON values") + wson::dumps(&Value::object([("error", error)]), wson::Format::Json, false) + .expect("diagnostic schema contains only shallow JSON values") } pub fn display_auto(&self) { @@ -305,10 +301,7 @@ impl WaveError { pub fn stdlib_requires_vex(module: &str, file: &str, line: usize, column: usize) -> Self { Self::new( WaveErrorKind::VexIntegrationRequired(module.to_string()), - format!( - "standard library module 'std::{}' requires Vex package manager", - module - ), + format!("standard library module 'std::{}' requires Vex package manager", module), file, line, column, @@ -334,10 +327,7 @@ impl WaveError { expected: expected.to_string(), found: found.to_string(), }, - format!( - "mismatched types: expected `{}`, found `{}`", - expected, found - ), + format!("mismatched types: expected `{}`, found `{}`", expected, found), file, line, column, @@ -380,10 +370,8 @@ impl WaveError { if let Some(source_code) = &self.source_code { // `lines()` drops the empty line containing EOF after a final newline. - let lines: Vec<&str> = crate::span::source_lines(source_code) - .into_iter() - .map(|(_, line)| line) - .collect(); + let lines: Vec<&str> = + crate::span::source_lines(source_code).into_iter().map(|(_, line)| line).collect(); if line <= lines.len() { let idx = line - 1; let start = idx.saturating_sub(1); @@ -395,23 +383,16 @@ impl WaveError { utils::display_width::diagnostic_line(source_line, col, self.span_len); let ln = i + 1; let ln_str = format!("{:>width$}", ln, width = width); - eprintln!( - " {} {} {}", - ln_str.color("38,139,235").bold(), - pipe, - rendered.text - ); + eprintln!(" {} {} {}", ln_str.color("38,139,235").bold(), pipe, rendered.text); if ln == line { let pad = " ".repeat(width); let spaces = " ".repeat(rendered.caret_offset); - let marks = "^" - .repeat(rendered.caret_width) - .color(self.severity_color()) - .bold(); + let marks = + "^".repeat(rendered.caret_width).color(self.severity_color()).bold(); match &self.label { Some(label) => { eprintln!(" {} {} {}{} {}", pad, pipe, spaces, marks, label.dim()) - } + }, None => eprintln!(" {} {} {}{}", pad, pipe, spaces, marks), } } @@ -424,19 +405,11 @@ impl WaveError { let rendered = utils::display_width::diagnostic_line(source_line, col, self.span_len); let width = line.to_string().len().max(2); let ln_str = format!("{:>width$}", line, width = width); - eprintln!( - " {} {} {}", - ln_str.color("38,139,235").bold(), - pipe, - rendered.text - ); + eprintln!(" {} {} {}", ln_str.color("38,139,235").bold(), pipe, rendered.text); let pad = " ".repeat(width); let spaces = " ".repeat(rendered.caret_offset); - let marks = "^" - .repeat(rendered.caret_width) - .color(self.severity_color()) - .bold(); + let marks = "^".repeat(rendered.caret_width).color(self.severity_color()).bold(); match &self.label { Some(label) => eprintln!(" {} {} {}{} {}", pad, pipe, spaces, marks, label.dim()), None => eprintln!(" {} {} {}{}", pad, pipe, spaces, marks), @@ -458,12 +431,7 @@ impl WaveError { let code = self .code .as_ref() - .map(|c| { - format!("[{}]", c) - .color(self.severity_color()) - .bold() - .to_string() - }) + .map(|c| format!("[{}]", c).color(self.severity_color()).bold().to_string()) .unwrap_or_default(); if code.is_empty() { @@ -585,14 +553,10 @@ impl WaveError { } if errors.len() > 1 { - let error_count = errors - .iter() - .filter(|e| matches!(e.severity, ErrorSeverity::Error)) - .count(); - let warning_count = errors - .iter() - .filter(|e| matches!(e.severity, ErrorSeverity::Warning)) - .count(); + let error_count = + errors.iter().filter(|e| matches!(e.severity, ErrorSeverity::Error)).count(); + let warning_count = + errors.iter().filter(|e| matches!(e.severity, ErrorSeverity::Warning)).count(); eprintln!(); if error_count > 0 { diff --git a/front/error/src/span.rs b/front/error/src/span.rs index b1daf264..4c78c2c2 100644 --- a/front/error/src/span.rs +++ b/front/error/src/span.rs @@ -19,12 +19,7 @@ impl SourceSpan { if self.file != last.file { return self.clone(); } - Self { - end: last.end, - end_line: last.end_line, - end_column: last.end_column, - ..self.clone() - } + Self { end: last.end, end_line: last.end_line, end_column: last.end_column, ..self.clone() } } pub fn generated(mut self, reason: impl Into) -> Self { diff --git a/front/error/tests/diagnostic_rendering.rs b/front/error/tests/diagnostic_rendering.rs index 30abdeca..5f7fdf06 100644 --- a/front/error/tests/diagnostic_rendering.rs +++ b/front/error/tests/diagnostic_rendering.rs @@ -4,14 +4,8 @@ use std::process::Command; use utils::wson::{self, Value}; fn error() -> WaveError { - WaveError::new( - WaveErrorKind::UnexpectedEndOfFile, - "missing closer", - "input.wave", - 2, - 1, - ) - .with_label("close the block") + WaveError::new(WaveErrorKind::UnexpectedEndOfFile, "missing closer", "input.wave", 2, 1) + .with_label("close the block") } #[test] @@ -22,21 +16,17 @@ fn renderer_fixture() { match case.as_str() { "batch" => { WaveError::display_batch(&[error(), error().with_severity(ErrorSeverity::Warning)]) - } + }, "empty-batch" => WaveError::display_batch(&[]), "single-batch" => WaveError::display_batch(&[error()]), "lf" => error().with_source_code("fun main() {\n").display(), "cr" => error().with_source_code("fun main() {\r").display(), "crlf" => error().with_source_code("fun main() {\r\n").display(), - "empty" => WaveError::new( - WaveErrorKind::UnexpectedEndOfFile, - "expected item", - "empty.wave", - 1, - 1, - ) - .with_source_code("") - .display(), + "empty" => { + WaveError::new(WaveErrorKind::UnexpectedEndOfFile, "expected item", "empty.wave", 1, 1) + .with_source_code("") + .display() + }, "invalid-line" => error().with_source_code("only one line").display(), _ => panic!("unknown fixture"), } @@ -63,10 +53,7 @@ fn json_preserves_labels_and_escapes_them() { &WaveError::new(WaveErrorKind::UnexpectedEndOfFile, "end", "x", 1, 1).to_json(), ) .unwrap(); - assert!(matches!( - value.get("error").unwrap().get("label"), - Some(Value::Null) - )); + assert!(matches!(value.get("error").unwrap().get("label"), Some(Value::Null))); } #[test] @@ -79,14 +66,8 @@ fn json_batches_contain_only_one_json_record_per_diagnostic() { .collect(); assert_eq!(records.len(), count, "{output}"); if count == 2 { - assert_eq!( - records[0].get("error").unwrap().get_str("severity"), - Some("error") - ); - assert_eq!( - records[1].get("error").unwrap().get_str("severity"), - Some("warning") - ); + assert_eq!(records[0].get("error").unwrap().get_str("severity"), Some("error")); + assert_eq!(records[1].get("error").unwrap().get_str("severity"), Some("warning")); } } } @@ -94,10 +75,7 @@ fn json_batches_contain_only_one_json_record_per_diagnostic() { #[test] fn human_batches_keep_summary_counts() { let output = rendered("batch", "human"); - assert!( - output.contains("error: aborting due to 1 previous error"), - "{output}" - ); + assert!(output.contains("error: aborting due to 1 previous error"), "{output}"); assert!(output.contains("warning: 1 warning emitted"), "{output}"); } @@ -106,10 +84,7 @@ fn eof_carets_render_on_empty_final_source_lines() { for case in ["lf", "crlf", "cr", "empty"] { let output = rendered(case, "human"); let lines: Vec<_> = output.lines().collect(); - let marker = lines - .iter() - .position(|line| line.contains('^')) - .expect("EOF needs a caret"); + let marker = lines.iter().position(|line| line.contains('^')).expect("EOF needs a caret"); let source_line = if case == "empty" { " 1 | " } else { " 2 | " }; assert_eq!(lines[marker - 1], source_line, "{output}"); assert!(!output.contains('\r'), "{output}"); diff --git a/front/hir/src/async_lower.rs b/front/hir/src/async_lower.rs index bd0e9245..c7c217bc 100644 --- a/front/hir/src/async_lower.rs +++ b/front/hir/src/async_lower.rs @@ -21,12 +21,7 @@ pub enum Transition { Jump(usize), Branch(Expression, usize, usize), Match(Expression, Vec<(MatchPattern, Vec, usize)>), - Await { - future: Expression, - output: String, - result: WaveType, - next: usize, - }, + Await { future: Expression, output: String, result: WaveType, next: usize }, Complete(Option), } #[derive(Debug, Clone)] @@ -68,11 +63,9 @@ pub fn plan( lower.block(&function.body)?; Ok(lower.frame) } + fn empty_state() -> AsyncState { - AsyncState { - operations: vec![], - transition: Transition::Complete(None), - } + AsyncState { operations: vec![], transition: Transition::Complete(None) } } struct Lower<'a> { program: &'a TypedProgram, @@ -84,12 +77,11 @@ struct Lower<'a> { fn var(name: impl Into) -> Expression { Expression::Variable(name.into()) } + fn field(name: impl Into) -> Expression { - Expression::FieldAccess { - object: Box::new(var("$async$frame")), - field: name.into(), - } + Expression::FieldAccess { object: Box::new(var("$async$frame")), field: name.into() } } + fn store(target: Expression, value: Expression) -> ASTNode { ASTNode::Statement(StatementNode::Expression(Expression::Assignment { target: Box::new(target), @@ -107,6 +99,7 @@ impl Lower<'_> { .cloned(), } } + fn ty( &self, e: &Expression, @@ -116,34 +109,36 @@ impl Lower<'_> { Some(HirExpressionType::Resolved(t)) => Ok(t.clone()), Some(HirExpressionType::IntegerLiteral) => { Ok(expected.cloned().unwrap_or(WaveType::Int(32))) - } + }, Some(HirExpressionType::FloatLiteral) => { Ok(expected.cloned().unwrap_or(WaveType::Float(32))) - } + }, _ => expected .cloned() .ok_or_else(|| self.error(e, "async temporary needs a concrete type")), } } + fn new_slot(&mut self, ty: WaveType) -> String { let name = format!("v{}", self.frame.slots.len()); - self.frame.slots.push(FrameSlot { - name: name.clone(), - ty, - }); + self.frame.slots.push(FrameSlot { name: name.clone(), ty }); name } + fn state(&mut self) -> usize { let id = self.frame.states.len(); self.frame.states.push(empty_state()); id } + fn emit(&mut self, n: ASTNode) { self.frame.states[self.current].operations.push(n); } + fn end(&mut self, t: Transition) { self.frame.states[self.current].transition = t; } + fn snapshot(&mut self, e: Expression, ty: WaveType) -> Expression { if ty == WaveType::Void { self.emit(ASTNode::Statement(StatementNode::Expression(e))); @@ -153,6 +148,7 @@ impl Lower<'_> { self.emit(store(field(&name), e)); field(name) } + fn place(&mut self, e: &Expression) -> Result { Ok(match e.unspanned() { Expression::Variable(n) => self.bindings.get(n).map_or_else(|| var(n), field), @@ -170,21 +166,15 @@ impl Lower<'_> { target_type: t, })) } - } - Expression::FieldAccess { - object, - field: name, - } => { + }, + Expression::FieldAccess { object, field: name } => { let obj = if matches!(self.ty(object, None)?, WaveType::Pointer(_)) { self.expr(object, None)? } else { self.place(object)? }; - Expression::FieldAccess { - object: Box::new(obj), - field: name.clone(), - } - } + Expression::FieldAccess { object: Box::new(obj), field: name.clone() } + }, Expression::IndexAccess { target, index } => { let obj = if matches!(self.ty(target, None)?, WaveType::Array(..)) { self.place(target)? @@ -192,14 +182,12 @@ impl Lower<'_> { self.expr(target, None)? }; let index = self.expr(index, None)?; - Expression::IndexAccess { - target: Box::new(obj), - index: Box::new(index), - } - } + Expression::IndexAccess { target: Box::new(obj), index: Box::new(index) } + }, _ => return Err(self.error(e, "unsupported address across async suspension")), }) } + fn expr( &mut self, e: &Expression, @@ -209,24 +197,16 @@ impl Lower<'_> { let value = match e.unspanned() { Expression::Await(inner) => { let future = self.expr(inner, None)?; - let output = self.new_slot(if ty == WaveType::Void { - WaveType::Byte - } else { - ty.clone() - }); + let output = + self.new_slot(if ty == WaveType::Void { WaveType::Byte } else { ty.clone() }); let wait = self.state(); let next = self.state(); self.end(Transition::Jump(wait)); self.current = wait; - self.end(Transition::Await { - future, - output: output.clone(), - result: ty, - next, - }); + self.end(Transition::Await { future, output: output.clone(), result: ty, next }); self.current = next; return Ok(field(output)); - } + }, Expression::Variable(n) => self.bindings.get(n).map_or_else(|| var(n), field), Expression::Literal(_) | Expression::Null => e.clone(), Expression::Grouped(inner) => return self.expr(inner, expected), @@ -244,7 +224,7 @@ impl Lower<'_> { target_type: t, })) } - } + }, Expression::Unary { operator, expr } => Expression::Unary { operator: operator.clone(), expr: Box::new(self.expr(expr, Some(&ty))?), @@ -256,7 +236,7 @@ impl Lower<'_> { Some(HirExpressionType::IntegerLiteral) => match target_type { WaveType::Int(_) | WaveType::Uint(_) | WaveType::Byte | WaveType::Char => { Some(target_type.clone()) - } + }, WaveType::Pointer(_) | WaveType::Float(_) => Some(WaveType::Int(64)), _ => None, }, @@ -268,19 +248,17 @@ impl Lower<'_> { if matches!(target_type, WaveType::Pointer(_)) => { Some(target_type.clone()) - } + }, _ => None, }; Expression::Cast { expr: Box::new(self.expr(expr, source_context.as_ref())?), target_type: target_type.clone(), } - } - Expression::BinaryExpression { - left, - operator, - right, - } if matches!(operator, Operator::LogicalAnd | Operator::LogicalOr) => { + }, + Expression::BinaryExpression { left, operator, right } + if matches!(operator, Operator::LogicalAnd | Operator::LogicalOr) => + { let value = self.expr(left, Some(&WaveType::Bool))?; let output = self.new_slot(WaveType::Bool); self.emit(store(field(&output), value.clone())); @@ -298,12 +276,8 @@ impl Lower<'_> { self.end(Transition::Jump(done)); self.current = done; return Ok(field(output)); - } - Expression::BinaryExpression { - left, - operator, - right, - } => { + }, + Expression::BinaryExpression { left, operator, right } => { let lt = self.ty( left, if ty == WaveType::Bool { @@ -323,26 +297,19 @@ impl Lower<'_> { operator: operator.clone(), right: Box::new(right), } - } - Expression::FunctionCall { - name, - type_args, - args, - } => { + }, + Expression::FunctionCall { name, type_args, args } => { let parameters = self .program .syntax() .iter() .find_map(|n| match n { ASTNode::Function(f) if &f.name == name => Some( - f.parameters - .iter() - .map(|p| p.param_type.clone()) - .collect::>(), + f.parameters.iter().map(|p| p.param_type.clone()).collect::>(), ), ASTNode::ExternFunction(f) if &f.name == name => { Some(f.params.iter().map(|p| p.1.clone()).collect()) - } + }, ASTNode::Struct(s) => s .methods .iter() @@ -361,30 +328,18 @@ impl Lower<'_> { .enumerate() .map(|(i, a)| self.expr(a, parameters.get(i))) .collect::>()?; - Expression::FunctionCall { - name: name.clone(), - type_args: type_args.clone(), - args, - } - } - Expression::MethodCall { - object, - name, - type_args, - args, - } => { + Expression::FunctionCall { name: name.clone(), type_args: type_args.clone(), args } + }, + Expression::MethodCall { object, name, type_args, args } => { let object = self.expr(object, None)?; - let args = args - .iter() - .map(|a| self.expr(a, None)) - .collect::>()?; + let args = args.iter().map(|a| self.expr(a, None)).collect::>()?; Expression::MethodCall { object: Box::new(object), name: name.clone(), type_args: type_args.clone(), args, } - } + }, Expression::StructLiteral { name, fields } => { let types = self .program @@ -407,25 +362,15 @@ impl Lower<'_> { }) .collect::>()?, } - } + }, Expression::ArrayLiteral(values) => { - let inner = if let WaveType::Array(t, _) = &ty { - Some(t.as_ref()) - } else { - None - }; + let inner = if let WaveType::Array(t, _) = &ty { Some(t.as_ref()) } else { None }; Expression::ArrayLiteral( - values - .iter() - .map(|v| self.expr(v, inner)) - .collect::>()?, + values.iter().map(|v| self.expr(v, inner)).collect::>()?, ) - } + }, Expression::IndexAccess { .. } => self.place(e)?, - Expression::FieldAccess { - object, - field: name, - } => Expression::FieldAccess { + Expression::FieldAccess { object, field: name } => Expression::FieldAccess { object: Box::new(self.expr(object, None)?), field: name.clone(), }, @@ -443,40 +388,33 @@ impl Lower<'_> { }))); let value = Box::new(self.expr(value, Some(&target_ty))?); if let Expression::AssignOperation { operator, .. } = e.unspanned() { - Expression::AssignOperation { - target, - operator: operator.clone(), - value, - } + Expression::AssignOperation { target, operator: operator.clone(), value } } else { Expression::Assignment { target, value } } - } - Expression::IncDec { kind, target } => Expression::IncDec { - kind: kind.clone(), - target: Box::new(self.place(target)?), - }, - Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => Expression::AsmBlock { - instructions: instructions.clone(), - inputs: inputs - .iter() - .map(|(n, e)| Ok((n.clone(), self.expr(e, None)?))) - .collect::>()?, - outputs: outputs - .iter() - .map(|(n, e)| Ok((n.clone(), self.place(e)?))) - .collect::>()?, - clobbers: clobbers.clone(), + }, + Expression::IncDec { kind, target } => { + Expression::IncDec { kind: kind.clone(), target: Box::new(self.place(target)?) } + }, + Expression::AsmBlock { instructions, inputs, outputs, clobbers } => { + Expression::AsmBlock { + instructions: instructions.clone(), + inputs: inputs + .iter() + .map(|(n, e)| Ok((n.clone(), self.expr(e, None)?))) + .collect::>()?, + outputs: outputs + .iter() + .map(|(n, e)| Ok((n.clone(), self.place(e)?))) + .collect::>()?, + clobbers: clobbers.clone(), + } }, Expression::Located { .. } => unreachable!(), }; Ok(self.snapshot(value, ty)) } + fn block(&mut self, nodes: &[ASTNode]) -> Result<(), AsyncLoweringError> { let bindings = self.bindings.clone(); for n in nodes { @@ -485,6 +423,7 @@ impl Lower<'_> { self.bindings = bindings; Ok(()) } + fn node(&mut self, n: &ASTNode) -> Result<(), AsyncLoweringError> { match n.unspanned() { ASTNode::Variable(v) => { @@ -498,31 +437,23 @@ impl Lower<'_> { if let Some(init) = init { self.emit(store(field(slot), init)); } - } + }, ASTNode::Statement(s) => match s { StatementNode::Return(e) => { let ty = self.frame.result.clone(); let value = e.as_ref().map(|e| self.expr(e, Some(&ty))).transpose()?; self.end(Transition::Complete(value)); self.current = self.state(); - } + }, StatementNode::Expression(e) => { self.expr(e, None)?; - } + }, StatementNode::Assign { variable, value } => { - let target = self - .bindings - .get(variable) - .map_or_else(|| var(variable), field); + let target = self.bindings.get(variable).map_or_else(|| var(variable), field); let value = self.expr(value, None)?; self.emit(store(target, value)); - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { let done = self.state(); let mut branches = vec![(condition, body.as_slice())]; if let Some(others) = else_if_blocks { @@ -543,16 +474,13 @@ impl Lower<'_> { } self.end(Transition::Jump(done)); self.current = done; - } + }, StatementNode::While { condition, body } => { self.loop_body(None, condition, None, body)? - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => self.loop_body(Some(initialization), condition, Some(increment), body)?, + }, + StatementNode::For { initialization, condition, increment, body } => { + self.loop_body(Some(initialization), condition, Some(increment), body)? + }, StatementNode::Break | StatementNode::Continue => { let (exit, next) = *self.loops.last().expect("validated loop"); self.end(Transition::Jump(if matches!(s, StatementNode::Break) { @@ -561,7 +489,7 @@ impl Lower<'_> { next })); self.current = self.state(); - } + }, StatementNode::Match { value, arms } => { let value_ty = self.ty(value, None)?; let value = self.expr(value, None)?; @@ -585,41 +513,26 @@ impl Lower<'_> { self.current = start; self.end(Transition::Match(value, branches)); self.current = done; - } + }, StatementNode::Input { format, args } => { - let args = args - .iter() - .map(|e| self.place(e)) - .collect::>()?; + let args = args.iter().map(|e| self.place(e)).collect::>()?; self.emit(ASTNode::Statement(StatementNode::Input { format: format.clone(), args, })); - } + }, StatementNode::PrintFormat { format, args } | StatementNode::PrintlnFormat { format, args } => { - let args = args - .iter() - .map(|e| self.expr(e, None)) - .collect::>()?; + let args = args.iter().map(|e| self.expr(e, None)).collect::>()?; let s = match s { - StatementNode::PrintFormat { .. } => StatementNode::PrintFormat { - format: format.clone(), - args, - }, - _ => StatementNode::PrintlnFormat { - format: format.clone(), - args, + StatementNode::PrintFormat { .. } => { + StatementNode::PrintFormat { format: format.clone(), args } }, + _ => StatementNode::PrintlnFormat { format: format.clone(), args }, }; self.emit(ASTNode::Statement(s)); - } - StatementNode::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => { + }, + StatementNode::AsmBlock { instructions, inputs, outputs, clobbers } => { let inputs = inputs .iter() .map(|(n, e)| Ok((n.clone(), self.expr(e, None)?))) @@ -634,42 +547,41 @@ impl Lower<'_> { outputs, clobbers: clobbers.clone(), })); - } + }, _ => self.emit(n.clone()), }, ASTNode::Expression(e) => { self.expr(e, None)?; - } + }, _ => { return Err(AsyncLoweringError { message: "unsupported declaration inside async function".into(), span: n.span().cloned(), }) - } + }, } Ok(()) } + fn bind_pattern(&mut self, p: &MatchPattern, ty: &WaveType) -> Result<(), AsyncLoweringError> { match p.unspanned() { MatchPattern::Binding(name) => { let slot = self.new_slot(ty.clone()); self.bindings.insert(name.clone(), slot.clone()); self.emit(store(field(slot), var(name))); - } + }, MatchPattern::Variant { payloads, .. } => { - let info = self - .program - .variant_pattern_of(p) - .expect("typed variant pattern"); + let info = self.program.variant_pattern_of(p).expect("typed variant pattern"); let types = info.payload_types.clone(); for (p, t) in payloads.iter().zip(types) { self.bind_pattern(p, &t)?; } - } - _ => {} + }, + _ => {}, } Ok(()) } + fn loop_body( &mut self, init: Option<&ASTNode>, @@ -708,27 +620,23 @@ impl Lower<'_> { fn integer(n: usize) -> Expression { Expression::Literal(Literal::Int(n.to_string())) } + fn call(name: &str, type_args: Vec, args: Vec) -> Expression { - Expression::FunctionCall { - name: name.into(), - type_args, - args, - } + Expression::FunctionCall { name: name.into(), type_args, args } } + fn statement(e: Expression) -> ASTNode { ASTNode::Statement(StatementNode::Expression(e)) } + fn ret(e: Expression) -> ASTNode { ASTNode::Statement(StatementNode::Return(Some(e))) } + fn parameter(name: &str, ty: WaveType) -> ParameterNode { - ParameterNode { - span: None, - name: name.into(), - param_type: ty, - initial_value: None, - } + ParameterNode { span: None, name: name.into(), param_type: ty, initial_value: None } } + fn function( name: String, parameters: Vec, @@ -748,12 +656,11 @@ fn function( visibility: Visibility::Private, } } + fn jump(next: usize) -> Vec { - vec![ - store(field("state"), integer(next)), - ASTNode::Statement(StatementNode::Continue), - ] + vec![store(field("state"), integer(next)), ASTNode::Statement(StatementNode::Continue)] } + fn emit_frame( original: &FunctionNode, frame: AsyncFrame, @@ -762,19 +669,11 @@ fn emit_frame( let frame_name = format!("$async$frame${id}"); let poll_name = format!("$async$poll${id}"); let frame_ty = WaveType::Struct(frame_name.clone()); - let mut fields = frame - .slots - .iter() - .map(|s| (s.name.clone(), s.ty.clone())) - .collect::>(); + let mut fields = frame.slots.iter().map(|s| (s.name.clone(), s.ty.clone())).collect::>(); fields.push(("state".into(), WaveType::Int(32))); fields.push(( "result".into(), - if frame.result == WaveType::Void { - WaveType::Byte - } else { - frame.result.clone() - }, + if frame.result == WaveType::Void { WaveType::Byte } else { frame.result.clone() }, )); let structure = ASTNode::Struct(StructNode { name: frame_name, @@ -796,7 +695,7 @@ fn emit_frame( else_if_blocks: None, else_block: Some(Box::new(jump(no))), })) - } + }, Transition::Match(value, branches) => { body.push(ASTNode::Statement(StatementNode::Match { value, @@ -804,21 +703,12 @@ fn emit_frame( .into_iter() .map(|(pattern, mut body, next)| { body.extend(jump(next)); - MatchArm { - span: None, - pattern, - body, - } + MatchArm { span: None, pattern, body } }) .collect(), })) - } - Transition::Await { - future, - output, - result, - next, - } => { + }, + Transition::Await { future, output, result, next } => { let take = call("__wave_async_take", vec![], vec![future.clone()]); let mut ready = vec![if result == WaveType::Void { statement(take) @@ -838,26 +728,18 @@ fn emit_frame( vec![var("$async$id"), future], ))); body.push(ret(Expression::Literal(Literal::Bool(false)))); - } + }, Transition::Complete(value) => { if frame.result != WaveType::Void { if let Some(value) = value { body.push(store(field("result"), value)); } } - body.push(statement(call( - "__wave_async_complete", - vec![], - vec![var("$async$id")], - ))); + body.push(statement(call("__wave_async_complete", vec![], vec![var("$async$id")]))); body.push(ret(Expression::Literal(Literal::Bool(true)))); - } + }, } - arms.push(MatchArm { - span: None, - pattern: MatchPattern::Int(index.to_string()), - body, - }); + arms.push(MatchArm { span: None, pattern: MatchPattern::Int(index.to_string()), body }); } arms.push(MatchArm { span: None, @@ -867,19 +749,13 @@ fn emit_frame( let resume = function( poll_name.clone(), vec![ - parameter( - "$async$frame", - WaveType::Pointer(Box::new(frame_ty.clone())), - ), + parameter("$async$frame", WaveType::Pointer(Box::new(frame_ty.clone()))), parameter("$async$id", WaveType::Int(64)), ], WaveType::Bool, vec![ASTNode::Statement(StatementNode::While { condition: Expression::Literal(Literal::Bool(true)), - body: vec![ASTNode::Statement(StatementNode::Match { - value: field("state"), - arms, - })], + body: vec![ASTNode::Statement(StatementNode::Match { value: field("state"), arms })], })], ); let mut constructor = original.clone(); @@ -920,7 +796,7 @@ pub fn lower_program(program: &TypedProgram) -> Result, AsyncLoweri next += 1; *out = ctor; generated.extend(extra); - } + }, (ASTNode::Struct(s), ASTNode::Struct(out)) if s.generic_params.is_empty() => { for (f, target) in s.methods.iter().zip(&mut out.methods) { if f.is_async && f.generic_params.is_empty() { @@ -930,7 +806,7 @@ pub fn lower_program(program: &TypedProgram) -> Result, AsyncLoweri generated.extend(extra); } } - } + }, (ASTNode::ProtoImpl(s), ASTNode::ProtoImpl(out)) => { for (f, target) in s.methods.iter().zip(&mut out.methods) { if f.is_async && f.generic_params.is_empty() { @@ -940,8 +816,8 @@ pub fn lower_program(program: &TypedProgram) -> Result, AsyncLoweri generated.extend(extra); } } - } - _ => {} + }, + _ => {}, } } output.extend(generated); diff --git a/front/hir/src/conversions.rs b/front/hir/src/conversions.rs index d44232bb..08d14373 100644 --- a/front/hir/src/conversions.rs +++ b/front/hir/src/conversions.rs @@ -79,18 +79,19 @@ pub fn integer_width(ty: &WaveType) -> Option { _ => None, } } + pub fn unsigned(ty: &WaveType) -> bool { - matches!( - ty, - WaveType::Uint(_) | WaveType::Bool | WaveType::Byte | WaveType::Char - ) + matches!(ty, WaveType::Uint(_) | WaveType::Bool | WaveType::Byte | WaveType::Char) } + pub fn numeric(ty: &WaveType) -> bool { integer_width(ty).is_some() || matches!(ty, WaveType::Float(_)) } + fn pointer(ty: &WaveType) -> bool { matches!(ty, WaveType::Pointer(_) | WaveType::String) } + pub(super) fn scalar(ty: &WaveType) -> bool { numeric(ty) || pointer(ty) } @@ -123,17 +124,13 @@ pub fn conversion_kind(source: &WaveType, target: &WaveType) -> Option { Some(if a < b { FloatExtend } else { FloatTruncate }) - } - (_, WaveType::Float(_)) if integer_width(source).is_some() => Some(if unsigned(source) { - UnsignedToFloat - } else { - SignedToFloat - }), - (WaveType::Float(_), _) if integer_width(target).is_some() => Some(if unsigned(target) { - FloatToUnsigned - } else { - FloatToSigned - }), + }, + (_, WaveType::Float(_)) if integer_width(source).is_some() => { + Some(if unsigned(source) { UnsignedToFloat } else { SignedToFloat }) + }, + (WaveType::Float(_), _) if integer_width(target).is_some() => { + Some(if unsigned(target) { FloatToUnsigned } else { FloatToSigned }) + }, _ if pointer(source) && integer_width(target).is_some() => Some(PointerToInteger), _ if integer_width(source).is_some() && pointer(target) => Some(IntegerToPointer), _ if pointer(source) && pointer(target) => Some(PointerCast), @@ -152,17 +149,11 @@ impl FloatIntegerRange { pub fn contains(&self, value: f64) -> bool { value.is_finite() && value < self.upper - && if self.lower_inclusive { - value >= self.lower - } else { - value > self.lower - } + && if self.lower_inclusive { value >= self.lower } else { value > self.lower } } } pub fn float_integer_range(source: &WaveType, target: &WaveType) -> FloatIntegerRange { - let WaveType::Float(source_bits) = source else { - panic!("non-float conversion source") - }; + let WaveType::Float(source_bits) = source else { panic!("non-float conversion source") }; let round = |v: f64| { if *source_bits == 32 { (v as f32) as f64 @@ -175,11 +166,7 @@ pub fn float_integer_range(source: &WaveType, target: &WaveType) -> FloatInteger let upper = round(2f64.powi(i32::from(bits - u16::from(signed)))); let minimum = if signed { -upper } else { 0.0 }; let before_minimum = round(minimum - 1.0); - FloatIntegerRange { - lower: before_minimum, - lower_inclusive: before_minimum == minimum, - upper, - } + FloatIntegerRange { lower: before_minimum, lower_inclusive: before_minimum == minimum, upper } } impl NumericExpressionInfo { @@ -200,11 +187,7 @@ impl NumericExpressionInfo { if previous != &self.result_type { return Err("conversion chain does not reach its result type".into()); } - if self - .context_type - .as_ref() - .is_some_and(|target| target != &self.result_type) - { + if self.context_type.as_ref().is_some_and(|target| target != &self.result_type) { return Err("conversion chain does not satisfy its contextual destination".into()); } Ok(()) @@ -222,24 +205,20 @@ fn comparison(op: &Operator) -> bool { | Operator::GreaterEqual ) } + fn literal(expr: &Expression) -> bool { match expr { Expression::Literal(Literal::Int(_) | Literal::Float(_)) => true, - Expression::Grouped(inner) - | Expression::Unary { - operator: Operator::Neg, - expr: inner, - } => literal(inner), + Expression::Grouped(inner) | Expression::Unary { operator: Operator::Neg, expr: inner } => { + literal(inner) + }, _ => false, } } pub(super) fn build(program: &TypedProgram) -> Vec> { - let mut planner = Planner { - program, - facts: vec![None; program.expression_count()], - seen: HashSet::new(), - }; + let mut planner = + Planner { program, facts: vec![None; program.expression_count()], seen: HashSet::new() }; super::walk_nodes(program.syntax(), &mut |expr| { if !planner.seen.contains(&program.expression_id(expr).unwrap()) { planner.plan(expr, program.expected_type_of(expr).cloned(), None); @@ -273,6 +252,7 @@ impl Planner<'_> { _ => None, } } + fn span(&self, expr: &Expression) -> SourceSpan { self.program .expression_id(expr) @@ -290,6 +270,7 @@ impl Planner<'_> { focus: None, }) } + fn append( &self, fact: &mut NumericExpressionInfo, @@ -311,6 +292,7 @@ impl Planner<'_> { } fact.result_type = target; } + fn plan( &mut self, expr: &Expression, @@ -330,10 +312,7 @@ impl Planner<'_> { shift_count_type: None, }; match expr { - Expression::Cast { - expr: inner, - target_type, - } => { + Expression::Cast { expr: inner, target_type } => { let hint = if matches!(self.program.type_of(inner), Some(HirExpressionType::Null)) { Some(target_type.clone()) } else { @@ -350,23 +329,14 @@ impl Planner<'_> { let inner_type = self.plan(inner, None, hint)?; fact.evaluation_type = inner_type.clone(); fact.result_type = inner_type; - self.append( - &mut fact, - target_type.clone(), - ConversionMode::Explicit, - expr, - ); - } + self.append(&mut fact, target_type.clone(), ConversionMode::Explicit, expr); + }, Expression::Grouped(inner) => { let inner_type = self.plan(inner, demand.clone(), literal_hint)?; fact.evaluation_type = inner_type.clone(); fact.result_type = inner_type; - } - Expression::BinaryExpression { - left, - operator, - right, - } => { + }, + Expression::BinaryExpression { left, operator, right } => { let left_hint = self.program.expected_type_of(left); let right_hint = self.program.expected_type_of(right); let mut lt = self.source_type(left, left_hint); @@ -383,17 +353,13 @@ impl Planner<'_> { } // A floating literal borrows a concrete floating operand's // width, including when the literal is on the left of a comparison. - if matches!( - self.program.type_of(left), - Some(HirExpressionType::FloatLiteral) - ) && matches!(rt, Some(WaveType::Float(_))) + if matches!(self.program.type_of(left), Some(HirExpressionType::FloatLiteral)) + && matches!(rt, Some(WaveType::Float(_))) { lt = rt.clone(); } - if matches!( - self.program.type_of(right), - Some(HirExpressionType::FloatLiteral) - ) && matches!(lt, Some(WaveType::Float(_))) + if matches!(self.program.type_of(right), Some(HirExpressionType::FloatLiteral)) + && matches!(lt, Some(WaveType::Float(_))) { rt = lt.clone(); } @@ -454,21 +420,14 @@ impl Planner<'_> { } } } - } - Expression::AssignOperation { - target, - value, - operator, - } => { + }, + Expression::AssignOperation { target, value, operator } => { let ty = self.source_type(target, None)?; self.plan(target, None, None); self.plan(value, Some(ty.clone()), None); fact.computation_type = (!matches!(operator, AssignOperator::Assign)).then_some(ty); - } - Expression::Unary { - operator, - expr: inner, - } => { + }, + Expression::Unary { operator, expr: inner } => { let input = if matches!(operator, Operator::Not | Operator::LogicalNot) { self.plan(inner, None, None)? } else { @@ -481,8 +440,8 @@ impl Planner<'_> { input }; fact.result_type = fact.evaluation_type.clone(); - } - _ => {} + }, + _ => {}, } if let Some(target) = demand.filter(scalar) { self.append(&mut fact, target, ConversionMode::Implicit, expr); @@ -517,10 +476,7 @@ pub(super) fn verify_expression( .ok_or_else(|| "missing scalar operand facts".to_string()) }; match expr { - Expression::Cast { - expr: inner, - target_type, - } => { + Expression::Cast { expr: inner, target_type } => { if child(inner)?.result_type != fact.evaluation_type { return Err("cast input differs from operand result".into()); } @@ -530,17 +486,13 @@ pub(super) fn verify_expression( if step.mode != ConversionMode::Explicit || &step.target_type != target_type { return Err("cast lost its explicit conversion".into()); } - } + }, Expression::Grouped(inner) => { if child(inner)?.result_type != fact.evaluation_type { return Err("group input differs from operand result".into()); } - } - Expression::BinaryExpression { - left, - operator, - right, - } => { + }, + Expression::BinaryExpression { left, operator, right } => { for operand in [left.as_ref(), right.as_ref()] { if matches!(program.type_of(operand), Some(HirExpressionType::Null)) || program.type_of(operand).is_some_and(|ty| match ty { @@ -552,10 +504,8 @@ pub(super) fn verify_expression( child(operand)?; } } - let operands = ( - program.numeric_expression_of(left), - program.numeric_expression_of(right), - ); + let operands = + (program.numeric_expression_of(left), program.numeric_expression_of(right)); if let (Some(left), Some(right)) = operands { if numeric(&left.result_type) && numeric(&right.result_type) { let Some(computation) = &fact.computation_type else { @@ -584,22 +534,16 @@ pub(super) fn verify_expression( if &left.result_type != computation || &right.result_type != computation { return Err("operand conversion does not reach computation type".into()); } - let result = if comparison(operator) { - &WaveType::Bool - } else { - computation - }; + let result = + if comparison(operator) { &WaveType::Bool } else { computation }; if &fact.evaluation_type != result { return Err("binary result disagrees with computation".into()); } } } } - } - Expression::Unary { - operator, - expr: inner, - } => { + }, + Expression::Unary { operator, expr: inner } => { let input = &child(inner)?.result_type; if fact.computation_type.as_ref() != Some(input) { return Err("unary computation differs from operand result".into()); @@ -612,12 +556,8 @@ pub(super) fn verify_expression( if &fact.evaluation_type != result { return Err("unary result disagrees with computation".into()); } - } - Expression::AssignOperation { - target, - value, - operator, - } => { + }, + Expression::AssignOperation { target, value, operator } => { let lhs = &child(target)?.result_type; if (!matches!(operator, AssignOperator::Assign) && fact.computation_type.as_ref() != Some(lhs)) @@ -625,8 +565,8 @@ pub(super) fn verify_expression( { return Err("compound assignment operands differ from computation type".into()); } - } - _ => {} + }, + _ => {}, } Ok(()) } @@ -636,18 +576,13 @@ mod tests { use super::*; use parser::ast::ASTNode; fn program(source: &str) -> TypedProgram { - let tokens = lexer::Lexer::new_with_file(source, "conversions.wave") - .tokenize() - .unwrap(); + let tokens = lexer::Lexer::new_with_file(source, "conversions.wave").tokenize().unwrap(); TypedProgram::lower(parser::parse_syntax_with_spans(&tokens).unwrap()).unwrap() } + fn initializer(p: &TypedProgram, index: usize) -> &Expression { - let ASTNode::Function(f) = &p.syntax()[0] else { - panic!() - }; - let ASTNode::Variable(v) = &f.body[index] else { - panic!() - }; + let ASTNode::Function(f) = &p.syntax()[0] else { panic!() }; + let ASTNode::Variable(v) = &f.body[index] else { panic!() }; v.initial_value.as_ref().unwrap() } #[test] @@ -656,11 +591,7 @@ mod tests { for source_bits in [32, 64] { for bits in [8, 16, 32, 64, 128, 256, 512, 1024] { for signed in [false, true] { - let target = if signed { - WaveType::Int(bits) - } else { - WaveType::Uint(bits) - }; + let target = if signed { WaveType::Int(bits) } else { WaveType::Uint(bits) }; let range = float_integer_range(&WaveType::Float(source_bits), &target); let edge = 2f64.powi(i32::from(bits - u16::from(signed))); let mut values = vec![ @@ -693,11 +624,7 @@ mod tests { } } for value in values { - let value = if source_bits == 32 { - (value as f32) as f64 - } else { - value - }; + let value = if source_bits == 32 { (value as f32) as f64 } else { value }; let exact = ConstInt::from_f64(value).is_some_and(|n| n.fits(bits, signed)); assert_eq!( range.contains(value), @@ -719,10 +646,7 @@ mod tests { assert_eq!(shift.shift_count_type, Some(WaveType::Uint(64))); assert_eq!(shift.computation_type, Some(WaveType::Uint(8))); assert_eq!( - p.numeric_expression_of(initializer(&p, 1)) - .unwrap() - .conversions[0] - .kind, + p.numeric_expression_of(initializer(&p, 1)).unwrap().conversions[0].kind, ConversionKind::IntegerToBool ); } @@ -733,9 +657,7 @@ mod tests { let p = program(source); p.verify_conversions().unwrap(); let outer = initializer(&p, 0); - let Expression::Cast { expr: inner, .. } = outer else { - panic!() - }; + let Expression::Cast { expr: inner, .. } = outer else { panic!() }; let small = p.numeric_expression_of(inner).unwrap(); assert_eq!( small.conversions.iter().map(|c| c.kind).collect::>(), @@ -754,17 +676,11 @@ mod tests { assert!(source[conversion.span.start..conversion.span.end].contains("as")); } assert_eq!( - p.numeric_expression_of(initializer(&p, 1)) - .unwrap() - .conversions[0] - .kind, + p.numeric_expression_of(initializer(&p, 1)).unwrap().conversions[0].kind, ConversionKind::ReinterpretInteger ); assert_eq!( - p.numeric_expression_of(initializer(&p, 2)) - .unwrap() - .conversions[0] - .kind, + p.numeric_expression_of(initializer(&p, 2)).unwrap().conversions[0].kind, ConversionKind::Identity ); } @@ -776,9 +692,7 @@ mod tests { assert_eq!(f.computation_type, Some(WaveType::Int(32))); assert_eq!(f.conversions[0].kind, ConversionKind::SignedToFloat); let sum = initializer(&p, 1); - let Expression::BinaryExpression { left, right, .. } = sum else { - panic!() - }; + let Expression::BinaryExpression { left, right, .. } = sum else { panic!() }; assert_eq!( p.numeric_expression_of(sum).unwrap().computation_type, Some(WaveType::Uint(32)) @@ -787,19 +701,14 @@ mod tests { p.numeric_expression_of(left).unwrap().conversions[0].kind, ConversionKind::SignExtend ); - assert_eq!( - p.numeric_expression_of(right).unwrap().result_type, - WaveType::Uint(32) - ); + assert_eq!(p.numeric_expression_of(right).unwrap().result_type, WaveType::Uint(32)); } #[test] fn verifier_rejects_missing_facts_disconnected_chains_wrong_kind_and_wrong_computation() { for mutation in 0..6 { let mut p = program("fun check(x: i32) { var y: i64 = x as u8 as i32; var z: i64 = x + x; }"); - let id = p - .expression_id(initializer(&p, if mutation == 4 { 1 } else { 0 })) - .unwrap(); + let id = p.expression_id(initializer(&p, if mutation == 4 { 1 } else { 0 })).unwrap(); let fact = p.numeric_expressions[id.index()].as_mut().unwrap(); match mutation { 0 => p.numeric_expressions[id.index()] = None, @@ -810,13 +719,10 @@ mod tests { 5 => { fact.conversions.pop(); fact.result_type = WaveType::Int(32); - } + }, _ => unreachable!(), } - assert!( - p.verify_conversions().is_err(), - "mutation {mutation} escaped verifier" - ); + assert!(p.verify_conversions().is_err(), "mutation {mutation} escaped verifier"); } } } diff --git a/front/hir/src/lib.rs b/front/hir/src/lib.rs index 6834f9f3..88351331 100644 --- a/front/hir/src/lib.rs +++ b/front/hir/src/lib.rs @@ -160,12 +160,8 @@ impl TypedProgram { expression_ids.insert(address, id); expression_spans.push(source_map.expressions.get(&address).cloned()); expected_types.push(analyzed_expected.get(&address).cloned()); - expression_types.push( - analyzed_types - .get(&address) - .cloned() - .unwrap_or(HirExpressionType::Unknown), - ); + expression_types + .push(analyzed_types.get(&address).cloned().unwrap_or(HirExpressionType::Unknown)); variant_constructions.push(analyzed_variants.get(&address).cloned()); }); @@ -212,29 +208,21 @@ impl TypedProgram { program.numeric_expressions = conversions::build(&program); program.constant_values = match numeric_checks::validate(&program) { Ok(values) => values, - Err(diagnostic) => { - return Err(HirLoweringError { - syntax: program.syntax, - diagnostic, - }) - } + Err(diagnostic) => return Err(HirLoweringError { syntax: program.syntax, diagnostic }), }; Ok(program) } pub fn constant_value_of(&self, expression: &Expression) -> Option<&ConstantValue> { - self.expression_id(expression) - .and_then(|id| self.constant_values.get(&id)) + self.expression_id(expression).and_then(|id| self.constant_values.get(&id)) } pub fn numeric_expression(&self, id: ExpressionId) -> Option<&NumericExpressionInfo> { - self.numeric_expressions - .get(id.index()) - .and_then(Option::as_ref) + self.numeric_expressions.get(id.index()).and_then(Option::as_ref) } + pub fn numeric_expression_of(&self, expression: &Expression) -> Option<&NumericExpressionInfo> { - self.expression_id(expression) - .and_then(|id| self.numeric_expression(id)) + self.expression_id(expression).and_then(|id| self.numeric_expression(id)) } /// Reject missing or inconsistent facts before entering a backend. pub fn verify_conversions(&self) -> Result<(), ConversionError> { @@ -249,10 +237,8 @@ impl TypedProgram { self.type_of(expr), Some(HirExpressionType::IntegerLiteral | HirExpressionType::FloatLiteral) ) - || (matches!( - self.type_of(expr), - Some(HirExpressionType::AddressedArrayLiteral) - ) && matches!(self.expected_type_of(expr), Some(WaveType::Pointer(_)))); + || (matches!(self.type_of(expr), Some(HirExpressionType::AddressedArrayLiteral)) + && matches!(self.expected_type_of(expr), Some(WaveType::Pointer(_)))); let result = match self.numeric_expression(id) { Some(fact) => conversions::verify_expression(self, expr, fact), None if required => Err("missing required scalar conversion facts".into()), @@ -324,6 +310,7 @@ impl TypedProgram { pub fn node_id(&self, node: &ASTNode) -> Option { self.node_ids.get(&(node as *const _ as usize)).copied() } + pub fn node_span(&self, id: NodeId) -> Option<&error::SourceSpan> { self.node_spans.get(id.index())?.as_ref() } @@ -345,9 +332,7 @@ impl TypedProgram { } pub fn expression_id(&self, expression: &Expression) -> Option { - self.expression_ids - .get(&(expression as *const Expression as usize)) - .copied() + self.expression_ids.get(&(expression as *const Expression as usize)).copied() } pub fn expression_type(&self, id: ExpressionId) -> Option<&HirExpressionType> { @@ -359,8 +344,7 @@ impl TypedProgram { } pub fn type_of(&self, expression: &Expression) -> Option<&HirExpressionType> { - self.expression_id(expression) - .and_then(|id| self.expression_type(id)) + self.expression_id(expression).and_then(|id| self.expression_type(id)) } pub fn variant_construction(&self, id: ExpressionId) -> Option<&HirVariantConstruction> { @@ -376,14 +360,11 @@ impl TypedProgram { &self, expression: &Expression, ) -> Option<&HirVariantConstruction> { - self.expression_id(expression) - .and_then(|id| self.variant_construction(id)) + self.expression_id(expression).and_then(|id| self.variant_construction(id)) } pub fn pattern_id(&self, pattern: &MatchPattern) -> Option { - self.pattern_ids - .get(&(pattern as *const MatchPattern as usize)) - .copied() + self.pattern_ids.get(&(pattern as *const MatchPattern as usize)).copied() } /// Validated decimal case value in the integer scrutinee's type. @@ -403,8 +384,7 @@ impl TypedProgram { /// Returns resolved variant metadata for a syntax pattern in this program. pub fn variant_pattern_of(&self, pattern: &MatchPattern) -> Option<&HirVariantPattern> { - self.pattern_id(pattern) - .and_then(|id| self.variant_pattern(id)) + self.pattern_id(pattern).and_then(|id| self.variant_pattern(id)) } } @@ -437,17 +417,14 @@ fn collect_named_types(nodes: &[ASTNode]) -> HashMap { match node.unspanned() { ASTNode::TypeAlias(alias) => { named.insert(alias.name.clone(), alias.target.clone()); - } + }, ASTNode::Enum(enumeration) => { named.insert(enumeration.name.clone(), enumeration.repr_type.clone()); - } + }, ASTNode::Variant(variant) => { - named.insert( - variant.name.clone(), - WaveType::Variant(variant.name.clone()), - ); - } - _ => {} + named.insert(variant.name.clone(), WaveType::Variant(variant.name.clone())); + }, + _ => {}, } } named @@ -463,13 +440,13 @@ fn canonical_type( WaveType::Usz => named.get("usz").cloned().unwrap_or(WaveType::Usz), WaveType::Future(inner) => { WaveType::Future(Box::new(canonical_type(inner, named, visiting))) - } + }, WaveType::Pointer(inner) => { WaveType::Pointer(Box::new(canonical_type(inner, named, visiting))) - } + }, WaveType::Array(inner, length) => { WaveType::Array(Box::new(canonical_type(inner, named, visiting)), *length) - } + }, WaveType::Struct(name) => canonical_named_type(name, named, visiting) .unwrap_or_else(|| WaveType::Struct(name.clone())), WaveType::Variant(name) => canonical_variant_application(name, named, visiting) @@ -540,7 +517,7 @@ fn display_wave_type(ty: &WaveType) -> String { WaveType::Pointer(inner) => format!("ptr<{}>", display_wave_type(inner)), WaveType::Array(inner, length) => { format!("array<{},{}>", display_wave_type(inner), length) - } + }, WaveType::Void => "void".to_string(), WaveType::Never => "!".to_string(), WaveType::Struct(name) | WaveType::Variant(name) => name.clone(), @@ -578,7 +555,7 @@ fn canonicalize_node_types(node: &mut ASTNode, named: &HashMap canonicalize_type(parameter_type, named); } canonicalize_type(&mut function.return_type, named); - } + }, ASTNode::Program(parameter) => canonicalize_type(&mut parameter.param_type, named), ASTNode::Statement(statement) => canonicalize_statement_types(statement, named), ASTNode::Variable(variable) => { @@ -586,7 +563,7 @@ fn canonicalize_node_types(node: &mut ASTNode, named: &HashMap if let Some(initializer) = &mut variable.initial_value { canonicalize_expression_types(initializer, named); } - } + }, ASTNode::Expression(expression) => canonicalize_expression_types(expression, named), ASTNode::Struct(structure) => { for (_, field_type) in &mut structure.fields { @@ -595,12 +572,12 @@ fn canonicalize_node_types(node: &mut ASTNode, named: &HashMap for method in &mut structure.methods { canonicalize_function_types(method, named); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &mut implementation.methods { canonicalize_function_types(method, named); } - } + }, ASTNode::TypeAlias(alias) => canonicalize_type(&mut alias.target, named), ASTNode::Enum(enumeration) => canonicalize_type(&mut enumeration.repr_type, named), ASTNode::Variant(variant) => { @@ -609,7 +586,7 @@ fn canonicalize_node_types(node: &mut ASTNode, named: &HashMap canonicalize_type(payload_type, named); } } - } + }, } } @@ -621,13 +598,8 @@ fn canonicalize_statement_types(statement: &mut StatementNode, named: &HashMap { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { canonicalize_expression_types(condition, named); for node in body { canonicalize_node_types(node, named); @@ -645,26 +617,21 @@ fn canonicalize_statement_types(statement: &mut StatementNode, named: &HashMap { + }, + StatementNode::For { initialization, condition, increment, body } => { canonicalize_node_types(initialization, named); canonicalize_expression_types(condition, named); canonicalize_expression_types(increment, named); for node in body { canonicalize_node_types(node, named); } - } + }, StatementNode::While { condition, body } => { canonicalize_expression_types(condition, named); for node in body { canonicalize_node_types(node, named); } - } + }, StatementNode::Match { value, arms } => { canonicalize_expression_types(value, named); for arm in arms { @@ -672,25 +639,23 @@ fn canonicalize_statement_types(statement: &mut StatementNode, named: &HashMap canonicalize_expression_types(value, named), - StatementNode::AsmBlock { - inputs, outputs, .. - } => { + StatementNode::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter_mut().chain(outputs.iter_mut()) { canonicalize_expression_types(expression, named); } - } + }, StatementNode::Return(Some(expression)) | StatementNode::Expression(expression) => { canonicalize_expression_types(expression, named); - } + }, StatementNode::Print(_) | StatementNode::Println(_) | StatementNode::Variable(_) | StatementNode::Import(_) | StatementNode::Break | StatementNode::Continue - | StatementNode::Return(None) => {} + | StatementNode::Return(None) => {}, } } @@ -704,23 +669,16 @@ fn canonicalize_expression_types(expression: &mut Expression, named: &HashMap { + }, + Expression::FunctionCall { type_args, args, .. } => { for type_argument in type_args { canonicalize_type(type_argument, named); } for argument in args { canonicalize_expression_types(argument, named); } - } - Expression::MethodCall { - object, - args, - type_args, - .. - } => { + }, + Expression::MethodCall { object, args, type_args, .. } => { for type_argument in type_args { canonicalize_type(type_argument, named); } @@ -728,7 +686,7 @@ fn canonicalize_expression_types(expression: &mut Expression, named: &HashMap { canonicalize_expression_types(inner, named); - } + }, Expression::Cast { expr, target_type } => { canonicalize_expression_types(expr, named); canonicalize_type(target_type, named); - } + }, Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } - | Expression::AssignOperation { - target: left, - value: right, - .. - } - | Expression::Assignment { - target: left, - value: right, - } => { + | Expression::IndexAccess { target: left, index: right } + | Expression::AssignOperation { target: left, value: right, .. } + | Expression::Assignment { target: left, value: right } => { canonicalize_expression_types(left, named); canonicalize_expression_types(right, named); - } + }, Expression::ArrayLiteral(values) => { for value in values { canonicalize_expression_types(value, named); } - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter_mut().chain(outputs.iter_mut()) { canonicalize_expression_types(expression, named); } - } - Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {} + }, + Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {}, } } @@ -780,18 +726,18 @@ fn walk_patterns_in_nodes(nodes: &[ASTNode], visit: &mut impl FnMut(&MatchPatter match node { ASTNode::Located { value, .. } => { walk_patterns_in_nodes(std::slice::from_ref(value), visit) - } + }, ASTNode::Function(function) => walk_patterns_in_nodes(&function.body, visit), ASTNode::Struct(structure) => { for method in &structure.methods { walk_patterns_in_nodes(&method.body, visit); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { walk_patterns_in_nodes(&method.body, visit); } - } + }, ASTNode::Statement(statement) => walk_patterns_in_statement(statement, visit), ASTNode::ExternFunction(_) | ASTNode::Program(_) @@ -799,19 +745,14 @@ fn walk_patterns_in_nodes(nodes: &[ASTNode], visit: &mut impl FnMut(&MatchPatter | ASTNode::Expression(_) | ASTNode::TypeAlias(_) | ASTNode::Enum(_) - | ASTNode::Variant(_) => {} + | ASTNode::Variant(_) => {}, } } } fn walk_patterns_in_statement(statement: &StatementNode, visit: &mut impl FnMut(&MatchPattern)) { match statement { - StatementNode::If { - body, - else_if_blocks, - else_block, - .. - } => { + StatementNode::If { body, else_if_blocks, else_block, .. } => { walk_patterns_in_nodes(body, visit); if let Some(blocks) = else_if_blocks { for (_, body) in blocks.iter() { @@ -821,22 +762,18 @@ fn walk_patterns_in_statement(statement: &StatementNode, visit: &mut impl FnMut( if let Some(body) = else_block { walk_patterns_in_nodes(body, visit); } - } - StatementNode::For { - initialization, - body, - .. - } => { + }, + StatementNode::For { initialization, body, .. } => { walk_patterns_in_nodes(std::slice::from_ref(initialization.as_ref()), visit); walk_patterns_in_nodes(body, visit); - } + }, StatementNode::While { body, .. } => walk_patterns_in_nodes(body, visit), StatementNode::Match { arms, .. } => { for arm in arms { walk_pattern(&arm.pattern, visit); walk_patterns_in_nodes(&arm.body, visit); } - } + }, StatementNode::Print(_) | StatementNode::PrintFormat { .. } | StatementNode::Println(_) @@ -849,7 +786,7 @@ fn walk_patterns_in_statement(statement: &StatementNode, visit: &mut impl FnMut( | StatementNode::Break | StatementNode::Continue | StatementNode::Return(_) - | StatementNode::Expression(_) => {} + | StatementNode::Expression(_) => {}, } } diff --git a/front/hir/src/numeric_checks.rs b/front/hir/src/numeric_checks.rs index a0739fc4..314d1a16 100644 --- a/front/hir/src/numeric_checks.rs +++ b/front/hir/src/numeric_checks.rs @@ -45,6 +45,7 @@ impl Number { _ => unreachable!("aggregate is not a numeric truth value"), } } + fn normalize(self, ty: &WaveType) -> Self { match (self, ty) { (Self::Int(n), WaveType::Float(32)) => Self::Float(n.to_f32() as f64), @@ -52,7 +53,7 @@ impl Number { (Self::Int(n), ty) if integer_width(ty).is_some() => { let bits = integer_width(ty).unwrap(); Self::Int(n.normalize(bits, !unsigned(ty))) - } + }, (Self::Float(n), WaveType::Float(32)) => Self::Float((n as f32) as f64), (n, _) => n, } @@ -84,29 +85,29 @@ impl Checker<'_> { values.push(self.eval(item, scope)?); } Some(Number::Array(values)) - } + }, Expression::StructLiteral { fields, .. } => { let mut values = HashMap::new(); for (name, item) in fields { values.insert(name.clone(), self.eval(item, scope)?); } Some(Number::Struct(values)) - } + }, Expression::FieldAccess { object, field } => { if let Some(Number::Struct(fields)) = self.eval(object, scope)? { fields.get(field).cloned().flatten() } else { None } - } + }, Expression::IndexAccess { target, index } => { match (self.eval(target, scope)?, self.eval(index, scope)?) { - (Some(Number::Array(items)), Some(Number::Int(index))) => index - .to_usize() - .and_then(|i| items.get(i).cloned().flatten()), + (Some(Number::Array(items)), Some(Number::Int(index))) => { + index.to_usize().and_then(|i| items.get(i).cloned().flatten()) + }, _ => None, } - } + }, Expression::Literal(Literal::Int(raw)) => { let parsed = lexer::number::IntegerLiteral::parse(raw).unwrap(); let mut n = @@ -122,43 +123,42 @@ impl Checker<'_> { n = n.negated(); } Some(Number::Int(n)) - } + }, Expression::Literal(Literal::Float(n)) => Some(Number::Float(*n)), Expression::Literal(Literal::Bool(n)) => { Some(Number::Int(ConstInt::from_u64((*n) as u64))) - } + }, Expression::Literal(Literal::Byte(n)) => { Some(Number::Int(ConstInt::from_u64((*n) as u64))) - } + }, Expression::Literal(Literal::Char(n)) => { Some(Number::Int(ConstInt::from_u64((*n as u32) as u64))) - } + }, Expression::Variable(name) => { if let Some(value) = scope.get(name) { value.clone() } else { self.globals.get(name).cloned().flatten() } - } + }, Expression::Cast { expr, .. } | Expression::Grouped(expr) => self.eval(expr, scope)?, - Expression::Unary { - operator, - expr: inner, - } => match (operator, self.eval(inner, scope)?) { - (Operator::Neg, Some(Number::Int(n))) => Some(Number::Int(n.negated())), - (Operator::Neg, Some(Number::Float(n))) => Some(Number::Float(-n)), - (Operator::BitwiseNot, Some(Number::Int(n))) => n.checked_not().map(Number::Int), - (Operator::Not | Operator::LogicalNot, Some(n)) => { - Some(Number::Int(ConstInt::from_u64((!n.truth()) as u64))) + Expression::Unary { operator, expr: inner } => { + match (operator, self.eval(inner, scope)?) { + (Operator::Neg, Some(Number::Int(n))) => Some(Number::Int(n.negated())), + (Operator::Neg, Some(Number::Float(n))) => Some(Number::Float(-n)), + (Operator::BitwiseNot, Some(Number::Int(n))) => { + n.checked_not().map(Number::Int) + }, + (Operator::Not | Operator::LogicalNot, Some(n)) => { + Some(Number::Int(ConstInt::from_u64((!n.truth()) as u64))) + }, + _ => None, } - _ => None, }, - Expression::BinaryExpression { - left, - operator, - right, - } => self.eval_binary(expr, left, operator, right, scope)?, + Expression::BinaryExpression { left, operator, right } => { + self.eval_binary(expr, left, operator, right, scope)? + }, _ => { let mut result = Ok(()); parser::ast::visit::walk_expression_children(expr, &mut |child| { @@ -168,7 +168,7 @@ impl Checker<'_> { }); result?; None - } + }, }) } @@ -186,11 +186,7 @@ impl Checker<'_> { (Some(n), Operator::LogicalOr) if n.truth() => Some(true), _ => None, }; - let b = if short.is_some() { - None - } else { - self.eval(right, scope)? - }; + let b = if short.is_some() { None } else { self.eval(right, scope)? }; self.binary_value(expr, right, operator, a, b, short) } @@ -262,43 +258,31 @@ impl Checker<'_> { GreaterEqual => Some(ConstInt::from_u64((a >= b) as u64)), LogicalAnd => { Some(ConstInt::from_u64((!a.is_zero() && !b.is_zero()) as u64)) - } + }, LogicalOr => { Some(ConstInt::from_u64((!a.is_zero() || !b.is_zero()) as u64)) - } + }, _ => None, }; n.map(Number::Int) - } + }, (Some(Number::Float(a)), Some(Number::Float(b))) => { use Operator::*; let single = fact.computation_type == Some(WaveType::Float(32)); let result = match operator { - Add => Some(if single { - ((a as f32) + (b as f32)) as f64 - } else { - a + b - }), - Subtract => Some(if single { - ((a as f32) - (b as f32)) as f64 - } else { - a - b - }), - Multiply => Some(if single { - ((a as f32) * (b as f32)) as f64 - } else { - a * b - }), - Divide => Some(if single { - ((a as f32) / (b as f32)) as f64 - } else { - a / b - }), - Remainder => Some(if single { - ((a as f32) % (b as f32)) as f64 - } else { - a % b - }), + Add => Some(if single { ((a as f32) + (b as f32)) as f64 } else { a + b }), + Subtract => { + Some(if single { ((a as f32) - (b as f32)) as f64 } else { a - b }) + }, + Multiply => { + Some(if single { ((a as f32) * (b as f32)) as f64 } else { a * b }) + }, + Divide => { + Some(if single { ((a as f32) / (b as f32)) as f64 } else { a / b }) + }, + Remainder => { + Some(if single { ((a as f32) % (b as f32)) as f64 } else { a % b }) + }, _ => None, }; match operator { @@ -310,7 +294,7 @@ impl Checker<'_> { GreaterEqual => Some(Number::Int(ConstInt::from_u64((a >= b) as u64))), _ => result.map(Number::Float), } - } + }, _ => None, } }) @@ -366,12 +350,12 @@ impl Checker<'_> { _ => None, }; if let Some(constant) = constant { - self.constants - .insert(self.program.expression_id(expr).unwrap(), constant); + self.constants.insert(self.program.expression_id(expr).unwrap(), constant); } } Ok(Some(value)) } + fn inspect(&mut self, expr: &Expression, scope: &Scope) -> Result<(), Failure> { self.eval(expr, scope).map(|_| ()) } @@ -396,7 +380,7 @@ impl Checker<'_> { Expression::Variable(name) => { self.globals.contains_key(name) || self.program.variant_construction_of(expr).is_some() - } + }, Expression::FunctionCall { .. } => self.program.variant_construction_of(expr).is_some(), _ => false, }; @@ -411,6 +395,7 @@ impl Checker<'_> { }); result } + fn function(&mut self, f: &FunctionNode) -> Result<(), Failure> { let mut scope = Scope::new(); for p in &f.parameters { @@ -421,12 +406,14 @@ impl Checker<'_> { } self.nodes(&f.body, &mut scope) } + fn nodes(&mut self, nodes: &[ASTNode], scope: &mut Scope) -> Result<(), Failure> { for node in nodes { self.node(node, scope)?; } Ok(()) } + fn node(&mut self, node: &ASTNode, scope: &mut Scope) -> Result<(), Failure> { match node { ASTNode::Located { value, .. } => self.node(value, scope)?, @@ -435,12 +422,12 @@ impl Checker<'_> { for f in &s.methods { self.function(f)?; } - } + }, ASTNode::ProtoImpl(p) => { for f in &p.methods { self.function(f)?; } - } + }, ASTNode::Variable(v) => { let mut value = None; if let Some(expr) = &v.initial_value { @@ -450,7 +437,7 @@ impl Checker<'_> { } } scope.insert(v.name.clone(), value); - } + }, ASTNode::Statement(StatementNode::If { condition, body, @@ -468,11 +455,11 @@ impl Checker<'_> { if let Some(b) = else_block { self.nodes(b, &mut scope.clone())?; } - } + }, ASTNode::Statement(StatementNode::While { condition, body }) => { self.inspect(condition, scope)?; self.nodes(body, &mut scope.clone())?; - } + }, ASTNode::Statement(StatementNode::For { initialization, condition, @@ -484,7 +471,7 @@ impl Checker<'_> { self.inspect(condition, &scope)?; self.inspect(increment, &scope)?; self.nodes(body, &mut scope)?; - } + }, ASTNode::Statement(StatementNode::Match { value, arms }) => { self.inspect(value, scope)?; for arm in arms { @@ -496,7 +483,7 @@ impl Checker<'_> { }); self.nodes(&arm.body, &mut scope)?; } - } + }, _ => { let mut result = Ok(()); let mut visited = HashSet::new(); @@ -510,7 +497,7 @@ impl Checker<'_> { } }); result?; - } + }, } Ok(()) } @@ -535,16 +522,12 @@ pub(super) fn validate( .filter_map(|(i, node)| match node { ASTNode::Variable(v) if v.mutability == Mutability::Const => { v.initial_value.as_ref().map(|e| (v.name.clone(), (i, e))) - } + }, _ => None, }) .collect(); - let mut checker = Checker { - program, - globals: Scope::new(), - constants: HashMap::new(), - required: true, - }; + let mut checker = + Checker { program, globals: Scope::new(), constants: HashMap::new(), required: true }; for node in program.syntax() { if let ASTNode::Enum(e) = node { let mut next = ConstInt::zero(); @@ -575,12 +558,8 @@ pub(super) fn validate( while let Some((name, ready)) = stack.pop() { let &(index, expr) = &definitions[&name]; if ready { - checker - .require_constant(expr) - .map_err(|e| diagnostic(index, e))?; - let value = checker - .eval(expr, &Scope::new()) - .map_err(|e| diagnostic(index, e))?; + checker.require_constant(expr).map_err(|e| diagnostic(index, e))?; + let value = checker.eval(expr, &Scope::new()).map_err(|e| diagnostic(index, e))?; checker.globals.insert(name, value); } else if visited.insert(name.clone()) { stack.push((name, true)); @@ -598,21 +577,15 @@ pub(super) fn validate( if let ASTNode::Variable(v) = node { if v.mutability == Mutability::Static { if let Some(expr) = &v.initial_value { - checker - .require_constant(expr) - .map_err(|e| diagnostic(index, e))?; - checker - .eval(expr, &Scope::new()) - .map_err(|e| diagnostic(index, e))?; + checker.require_constant(expr).map_err(|e| diagnostic(index, e))?; + checker.eval(expr, &Scope::new()).map_err(|e| diagnostic(index, e))?; } } } } checker.required = false; for (index, node) in program.syntax().iter().enumerate() { - checker - .node(node, &mut Scope::new()) - .map_err(|e| diagnostic(index, e))?; + checker.node(node, &mut Scope::new()).map_err(|e| diagnostic(index, e))?; } Ok(checker.constants) } diff --git a/front/hir/tests/alpha_frontend.rs b/front/hir/tests/alpha_frontend.rs index c18755a9..a07ed351 100644 --- a/front/hir/tests/alpha_frontend.rs +++ b/front/hir/tests/alpha_frontend.rs @@ -24,15 +24,8 @@ fn rejects_unterminated_and_unsupported_statements_at_every_depth() { "var x: i32 = asm { ? };", ] { for nested in [false, true] { - let body = if nested { - format!("if (true) {{ {body} }}") - } else { - body.into() - }; - assert!( - syntax(&format!("fun main() {{ {body} }}")).is_err(), - "{body}" - ); + let body = if nested { format!("if (true) {{ {body} }}") } else { body.into() }; + assert!(syntax(&format!("fun main() {{ {body} }}")).is_err(), "{body}"); } } assert!(syntax("fun main() { asm { \"nop\"").is_err()); @@ -57,22 +50,12 @@ fn expression_statements_have_the_same_grammar_in_all_blocks() { #[test] fn numeric_defaults_preserve_radix_and_large_values() { - for value in [ - "0x10", - "0b10000", - "0o20", - "16", - "1_024", - "-0x10", - "18446744073709551616", - ] { + for value in ["0x10", "0b10000", "0o20", "16", "1_024", "-0x10", "18446744073709551616"] { let nodes = syntax(&format!( "fun value(x: i128 = {value}) -> i128 {{ return x; }} fun main() {{ value(); }}" )) .unwrap(); - let ASTNode::Function(f) = &nodes[0] else { - panic!() - }; + let ASTNode::Function(f) = &nodes[0] else { panic!() }; assert!(matches!( &f.parameters[0].initial_value, Some(Expression::Literal(Literal::Int(_))) @@ -102,10 +85,7 @@ fn numeric_defaults_preserve_radix_and_large_values() { "x: i32 y: i32", "x: i32 = unknown", ] { - assert!( - syntax(&format!("fun f({declaration}) {{}} ")).is_err(), - "{declaration}" - ); + assert!(syntax(&format!("fun f({declaration}) {{}} ")).is_err(), "{declaration}"); } } @@ -145,30 +125,11 @@ fn target_sized_types_resolve_recursively_without_host_assumptions() { use hir::resolve_target_types; use parser::ast::WaveType; use parser::types::{parse_type, token_type_to_wave_type}; - assert_eq!( - token_type_to_wave_type(&parse_type("isz").unwrap()), - Some(WaveType::Isz) - ); - assert_eq!( - token_type_to_wave_type(&parse_type("usz").unwrap()), - Some(WaveType::Usz) - ); - for invalid in [ - "i0", - "i1", - "i24", - "u7", - "u2048", - "i9999999999999", - "f16", - "f128", - "i032", - ] { + assert_eq!(token_type_to_wave_type(&parse_type("isz").unwrap()), Some(WaveType::Isz)); + assert_eq!(token_type_to_wave_type(&parse_type("usz").unwrap()), Some(WaveType::Usz)); + for invalid in ["i0", "i1", "i24", "u7", "u2048", "i9999999999999", "f16", "f128", "i032"] { assert!(parse_type(invalid).is_none(), "{invalid}"); - assert!( - syntax(&format!("fun f(x: {invalid}) {{}} ")).is_err(), - "{invalid}" - ); + assert!(syntax(&format!("fun f(x: {invalid}) {{}} ")).is_err(), "{invalid}"); } for name in ["item", "user", "file", "이름", "pkg::item"] { assert!(parse_type(name).is_some()); @@ -177,18 +138,13 @@ fn target_sized_types_resolve_recursively_without_host_assumptions() { for bits in [32, 64] { let mut nodes = syntax(source).unwrap(); resolve_target_types(&mut nodes, bits).unwrap(); - let ASTNode::Function(f) = &nodes[2] else { - panic!() - }; + let ASTNode::Function(f) = &nodes[2] else { panic!() }; assert_eq!(f.return_type, Some(WaveType::Int(bits))); assert_eq!( f.parameters[0].param_type, WaveType::Pointer(Box::new(WaveType::Array(Box::new(WaveType::Int(bits)), 2))) ); - assert_eq!( - f.parameters[1].param_type, - WaveType::Struct(format!("Box")) - ); + assert_eq!(f.parameters[1].param_type, WaveType::Struct(format!("Box"))); TypedProgram::lower(monomorphize_generics(nodes).unwrap()).unwrap(); } } @@ -210,22 +166,16 @@ fn never_returning_functions_must_terminate_without_returning() { "fun bad() { var x: !; }", "type Bad = !;", ] { - assert!( - TypedProgram::lower(syntax(source).unwrap()).is_err(), - "{source}" - ); + assert!(TypedProgram::lower(syntax(source).unwrap()).is_err(), "{source}"); } } #[test] fn asm_operands_keep_casts_and_projections_instead_of_skipping_tokens() { let nodes = syntax("fun f() { asm { in(\"r\") p as i64 out(\"r\") value.field } }").unwrap(); - let ASTNode::Function(f) = &nodes[0] else { - panic!() - }; - let ASTNode::Statement(parser::ast::StatementNode::AsmBlock { - inputs, outputs, .. - }) = &f.body[0] + let ASTNode::Function(f) = &nodes[0] else { panic!() }; + let ASTNode::Statement(parser::ast::StatementNode::AsmBlock { inputs, outputs, .. }) = + &f.body[0] else { panic!() }; diff --git a/front/hir/tests/async_frontend.rs b/front/hir/tests/async_frontend.rs index 616caf64..7afb070e 100644 --- a/front/hir/tests/async_frontend.rs +++ b/front/hir/tests/async_frontend.rs @@ -9,6 +9,7 @@ use parser::{ fn syntax(s: &str) -> Vec { parse_syntax_with_spans(&Lexer::new_with_file(s, "async.wave").tokenize().unwrap()).unwrap() } + fn typed(s: &str) -> TypedProgram { TypedProgram::lower(monomorphize_generics(syntax(s)).unwrap()).unwrap() } @@ -60,10 +61,7 @@ fn rejects_invalid_async_declarations_and_operands() { for (source, needle) in [ ("fun f() { await 1; }", "only valid inside"), ("async fun f() { await 1; }", "requires a Future"), - ( - "async fun f() -> i64 { return 1; } fun main(){var v:i64=f();}", - "Future", - ), + ("async fun f() -> i64 { return 1; } fun main(){var v:i64=f();}", "Future"), ("extern(c) fun send(f:Future); fun main(){}", "FFI"), ("type Bad = Future; fun main(){}", "cyclic"), ] { diff --git a/front/hir/tests/generic_methods.rs b/front/hir/tests/generic_methods.rs index 3f1627ed..5efb5cf5 100644 --- a/front/hir/tests/generic_methods.rs +++ b/front/hir/tests/generic_methods.rs @@ -4,9 +4,7 @@ use parser::generics::monomorphize_generics; use parser::{ast::ASTNode, parse_syntax_with_spans}; fn specialize(source: &str) -> Result, String> { - let tokens = Lexer::new_with_file(source, "methods.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "methods.wave").tokenize().unwrap(); monomorphize_generics(parse_syntax_with_spans(&tokens).unwrap()) } @@ -100,10 +98,7 @@ fn nested_method_receivers_have_resolved_hir_types() { panic!("result"); }; let expression = result.initial_value.as_ref().unwrap(); - assert_eq!( - program.type_of(expression), - Some(&HirExpressionType::Resolved(WaveType::Int(32))) - ); + assert_eq!(program.type_of(expression), Some(&HirExpressionType::Resolved(WaveType::Int(32)))); let Expression::MethodCall { object, .. } = expression else { panic!("read call"); }; diff --git a/front/hir/tests/io_match_contracts.rs b/front/hir/tests/io_match_contracts.rs index 8243a4e0..97e4fc7c 100644 --- a/front/hir/tests/io_match_contracts.rs +++ b/front/hir/tests/io_match_contracts.rs @@ -3,9 +3,8 @@ use lexer::Lexer; use parser::{generics::monomorphize_generics, parse_syntax_with_spans}; fn validate(source: &str) -> Result { - let tokens = Lexer::new_with_file(source, "contracts.wave") - .tokenize() - .map_err(|e| format!("{e:?}"))?; + let tokens = + Lexer::new_with_file(source, "contracts.wave").tokenize().map_err(|e| format!("{e:?}"))?; let ast = parse_syntax_with_spans(&tokens).map_err(|e| format!("{e:?}"))?; let ast = monomorphize_generics(ast)?; TypedProgram::lower(ast).map_err(|e| format!("{e:?}")) @@ -19,10 +18,7 @@ fn format_checks_happen_before_backend_lowering() { (r#"fun main() { print("{d}", "text"); }"#, "incompatible"), (r#"fun main() { print("{p}", 12); }"#, "incompatible"), (r#"fun main() { print("{c}", 1.2); }"#, "incompatible"), - ( - r#"fun main() { var n: i32 = 0; input("{x}", n); }"#, - "unsupported format placeholder", - ), + (r#"fun main() { var n: i32 = 0; input("{x}", n); }"#, "unsupported format placeholder"), (r#"fun main() { print("{} {}", 1); }"#, "arguments"), (r#"fun main() { println("{}", 1, 2); }"#, "arguments"), ] { @@ -65,19 +61,14 @@ fn integer_patterns_are_range_checked_and_deduplicated_in_the_scrutinee_type() { ) .unwrap(); let pattern = format!("0x8{}", "0".repeat(255)); - validate(&format!( - "fun f(n: u1024) {{ match (n) {{ {pattern} => {{}} _ => {{}} }} }}" - )) - .unwrap(); + validate(&format!("fun f(n: u1024) {{ match (n) {{ {pattern} => {{}} _ => {{}} }} }}")) + .unwrap(); } #[test] fn invalid_defaulted_method_arguments_are_still_rejected() { let prefix = "struct S { fun f(self: S, n: i32 = 1) -> i32 { return n; } }"; for call in ["s.f(1, 2)", "s.f(\"bad\")"] { - assert!(validate(&format!( - "{prefix} fun main() {{ var s: S = S {{}}; {call}; }}" - )) - .is_err()); + assert!(validate(&format!("{prefix} fun main() {{ var s: S = S {{}}; {call}; }}")).is_err()); } } diff --git a/front/hir/tests/parse_var_and_generics.rs b/front/hir/tests/parse_var_and_generics.rs index b072662b..2bb7f11d 100644 --- a/front/hir/tests/parse_var_and_generics.rs +++ b/front/hir/tests/parse_var_and_generics.rs @@ -18,10 +18,7 @@ fn parse_ok(src: &str) { idx, t.line, t.token_type, t.lexeme )); } - panic!( - "parse failed: {:?}\nsource:\n{}\ntokens:\n{}", - err, src, dump - ); + panic!("parse failed: {:?}\nsource:\n{}\ntokens:\n{}", err, src, dump); } } @@ -226,15 +223,9 @@ fn rejects_untyped_var_declarations() { let mut lexer = Lexer::new("fun main() { var value = 1; }\n"); let tokens = lexer.tokenize().expect("lex should succeed"); let error = parse_syntax_only(&tokens).expect_err("untyped variables must fail"); - assert_eq!( - error.message(), - "variable `value` requires an explicit type" - ); + assert_eq!(error.message(), "variable `value` requires an explicit type"); assert_eq!(error.context(), Some("variable declaration")); - assert_eq!( - error.help(), - Some("Wave does not infer variable types; add a `: Type` annotation") - ); + assert_eq!(error.help(), Some("Wave does not infer variable types; add a `: Type` annotation")); } #[test] diff --git a/front/hir/tests/phase1_frontend.rs b/front/hir/tests/phase1_frontend.rs index 2bd32d98..1ddcbfa8 100644 --- a/front/hir/tests/phase1_frontend.rs +++ b/front/hir/tests/phase1_frontend.rs @@ -9,9 +9,7 @@ use parser::{ fn syntax_error(marked: &str, context: &str, expected: Option<&str>) { let start = marked.find('@').unwrap(); let source = marked.replace('@', ""); - let tokens = Lexer::new_with_file(&source, "phase1.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "phase1.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert_eq!(error.context(), Some(context), "{source}: {error:?}"); assert_eq!(error.span().unwrap().start, start, "{source}: {error:?}"); @@ -34,9 +32,8 @@ fn local_storage_failures_identify_the_qualifier() { Some("var declaration or expression"), ); } - let tokens = Lexer::new("const c: i32 = 1; static s: i32 = 2; fun main() {}") - .tokenize() - .unwrap(); + let tokens = + Lexer::new("const c: i32 = 1; static s: i32 = 2; fun main() {}").tokenize().unwrap(); TypedProgram::lower(parse_syntax_with_spans(&tokens).unwrap()).unwrap(); } @@ -50,23 +47,11 @@ fn malformed_match_patterns_and_arms_retain_their_local_cause() { ("(x) { 1 @{} }", "'=>'", "match arm"), ("(x) { 1 = @{} }", "'=>'", "match arm"), ("(x) { 1 => @return; }", "'{'", "match arm body"), - ( - "(x) { V::A(@,) => {} }", - "integer literal, case name, or '_'", - "match pattern", - ), + ("(x) { V::A(@,) => {} }", "integer literal, case name, or '_'", "match pattern"), ] { - syntax_error( - &format!("fun f() {{ match {body} }}"), - context, - Some(expected), - ); + syntax_error(&format!("fun f() {{ match {body} }}"), context, Some(expected)); } - syntax_error( - "fun f() { match (x) { _ => {} @_ => {} } }", - "match pattern", - None, - ); + syntax_error("fun f() { match (x) { _ => {} @_ => {} } }", "match pattern", None); } #[test] @@ -77,11 +62,7 @@ fn extern_types_preserve_the_type_start_in_single_and_block_forms() { ("fun f() -> @array;", "extern return type"), ] { syntax_error(&format!("extern(c) {declaration}"), context, Some("type")); - syntax_error( - &format!("extern(c) {{ {declaration} }}"), - context, - Some("type"), - ); + syntax_error(&format!("extern(c) {{ {declaration} }}"), context, Some("type")); } } @@ -103,15 +84,9 @@ fn aliases_are_transparent_for_reads_writes_and_invalid_accesses() { let tokens = Lexer::new(source).tokenize().unwrap(); TypedProgram::lower(parse_syntax_with_spans(&tokens).unwrap()).unwrap(); for (source, message) in [ - ( - "type N = i32; fun f(x: N) { x[0]; }", - "index access requires", - ), + ("type N = i32; fun f(x: N) { x[0]; }", "index access requires"), ("type N = i32; fun f(x: N) { deref x; }", "deref expects"), - ( - "struct S { a: i32; } type A = S; fun f(x: A) { x.missing; }", - "has no field", - ), + ("struct S { a: i32; } type A = S; fun f(x: A) { x.missing; }", "has no field"), ] { let tokens = Lexer::new(source).tokenize().unwrap(); let error = TypedProgram::lower(parse_syntax_with_spans(&tokens).unwrap()).unwrap_err(); @@ -148,9 +123,7 @@ fn nested_generic_type_grammar_is_exact_and_rejects_malformed_input() { ); assert_eq!( token_type_to_wave_type(&parse_type("pkg::Pair, array>").unwrap()), - Some(WaveType::Struct( - "pkg::Pair, array>".into() - )) + Some(WaveType::Struct("pkg::Pair, array>".into())) ); assert_eq!( split_top_level_generic_args("ptr>, pkg::T"), diff --git a/front/hir/tests/source_spans.rs b/front/hir/tests/source_spans.rs index b2d89dc7..fa5d742f 100644 --- a/front/hir/tests/source_spans.rs +++ b/front/hir/tests/source_spans.rs @@ -7,42 +7,26 @@ use parser::parse_syntax_with_spans; use parser::verification::validate_program_detailed; fn parse(source: &str) -> Vec { - let tokens = Lexer::new_with_file(source, "unicode.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "unicode.wave").tokenize().unwrap(); parse_syntax_with_spans(&tokens).unwrap() } #[test] fn tokens_preserve_raw_spelling_bytes_and_unicode_columns() { let source = "// 🙂\r\nfun 이름() { \"a\\n\"; 이름; }"; - let tokens = Lexer::new_with_file(source, "unicode.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "unicode.wave").tokenize().unwrap(); for token in &tokens { let span = token.span.as_ref().unwrap(); assert_eq!(&source[span.start..span.end], token.lexeme); assert_eq!(span.file, "unicode.wave"); + assert_eq!(source[..span.start].bytes().filter(|b| *b == b'\n').count() + 1, span.line); assert_eq!( - source[..span.start].bytes().filter(|b| *b == b'\n').count() + 1, - span.line - ); - assert_eq!( - source[..span.start] - .rsplit('\n') - .next() - .unwrap() - .chars() - .count() - + 1, + source[..span.start].rsplit('\n').next().unwrap().chars().count() + 1, span.column ); } let string = tokens.iter().find(|t| t.lexeme == "\"a\\n\"").unwrap(); - assert_eq!( - string.span.as_ref().unwrap().end - string.span.as_ref().unwrap().start, - 5 - ); + assert_eq!(string.span.as_ref().unwrap().end - string.span.as_ref().unwrap().start, 5); } #[test] @@ -54,27 +38,20 @@ fn semantic_errors_identify_the_failing_occurrence() { assert_eq!(span.column, source[..span.start].chars().count() + 1); let source = "fun main() { var x: i32 = 1; x; missing; missing; }"; let diagnostic = validate_program_detailed(&parse(source)).unwrap_err(); - assert_eq!( - diagnostic.span.unwrap().start, - source.find("missing").unwrap() - ); + assert_eq!(diagnostic.span.unwrap().start, source.find("missing").unwrap()); } #[test] fn hir_preserves_all_binary_operand_occurrences_and_default_origins() { let source = "fun sum(x: i32 = 16) -> i32 { return x + x + x; } fun main() { sum(); }"; let hir = TypedProgram::lower(monomorphize_generics(parse(source)).unwrap()).unwrap(); - let ASTNode::Function(sum) = &hir.syntax()[0] else { - panic!() - }; + let ASTNode::Function(sum) = &hir.syntax()[0] else { panic!() }; let ASTNode::Statement(StatementNode::Return(Some(expression))) = &sum.body[0] else { panic!() }; fn visit(hir: &TypedProgram, expression: &Expression, spans: &mut Vec) { let id = hir.expression_id(expression).unwrap(); - let span = hir - .expression_span(id) - .expect("every physical expression has a span"); + let span = hir.expression_span(id).expect("every physical expression has a span"); if let Expression::Variable(_) = expression { spans.push(span.start); } @@ -87,26 +64,20 @@ fn hir_preserves_all_binary_operand_occurrences_and_default_origins() { visit(&hir, expression, &mut spans); assert_eq!(spans.len(), 3); assert!(spans.windows(2).all(|w| w[0] < w[1])); - let ASTNode::Function(main) = &hir.syntax()[1] else { - panic!() - }; + let ASTNode::Function(main) = &hir.syntax()[1] else { panic!() }; let ASTNode::Statement(StatementNode::Expression(Expression::FunctionCall { args, .. })) = &main.body[0] else { panic!() }; - let span = hir - .expression_span(hir.expression_id(&args[0]).unwrap()) - .unwrap(); + let span = hir.expression_span(hir.expression_id(&args[0]).unwrap()).unwrap(); assert_eq!(span.start, source.find("16").unwrap()); } #[test] fn syntax_errors_point_to_unexpected_token_not_function_start() { let source = "fun main() { 1 ? 2; }"; - let tokens = Lexer::new_with_file(source, "unicode.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "unicode.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert_eq!(error.span().unwrap().start, source.find('?').unwrap()); } @@ -117,17 +88,11 @@ fn target_filter_preserves_byte_offsets_including_crlf_and_unicode() { let source = "#[target(arch=\"arm64\")]\r\nvariant 이름 {\r\n Value(i32),\r\n}\r\nfun main() { missing; }\r\n"; let filtered = preprocess_target_attrs( source, - &TargetConditionContext { - arch: Some("amd64".into()), - ..Default::default() - }, + &TargetConditionContext { arch: Some("amd64".into()), ..Default::default() }, ); assert_eq!(source.len(), filtered.len()); let diagnostic = validate_program_detailed(&parse(&filtered)).unwrap_err(); - assert_eq!( - diagnostic.span.unwrap().start, - source.find("missing").unwrap() - ); + assert_eq!(diagnostic.span.unwrap().start, source.find("missing").unwrap()); } #[test] @@ -142,9 +107,7 @@ fn generic_instances_and_synthetic_nodes_have_explicit_provenance() { } let span = hir.node_span(hir.node_id(node).unwrap()).unwrap(); assert!(!span.expansion.is_empty()); - let ASTNode::Statement(StatementNode::Return(Some(value))) = &f.body[0] else { - panic!() - }; + let ASTNode::Statement(StatementNode::Return(Some(value))) = &f.body[0] else { panic!() }; let id = hir.expression_id(value).unwrap(); let span = hir.expression_span(id).unwrap(); assert_eq!(&source[span.start..span.end], "x"); @@ -155,9 +118,7 @@ fn generic_instances_and_synthetic_nodes_have_explicit_provenance() { assert_ne!(ids[0], ids[1]); let tokens = Lexer::new("fun main() {}").tokenize().unwrap(); let synthetic = TypedProgram::lower(parser::parse_syntax_only(&tokens).unwrap()).unwrap(); - assert!(synthetic - .node_span(synthetic.node_id(&synthetic.syntax()[0]).unwrap()) - .is_none()); + assert!(synthetic.node_span(synthetic.node_id(&synthetic.syntax()[0]).unwrap()).is_none()); } #[test] @@ -166,21 +127,13 @@ fn variant_pattern_ids_retain_recursive_source_ranges() { let source = "variant V { A(i32), B } fun f(v: V) { match (v) { V::A(x) => { x; }, V::B => {} } }"; let hir = TypedProgram::lower(monomorphize_generics(parse(source)).unwrap()).unwrap(); - let ASTNode::Function(f) = &hir.syntax()[1] else { - panic!() - }; - let ASTNode::Statement(StatementNode::Match { arms, .. }) = &f.body[0] else { - panic!() - }; + let ASTNode::Function(f) = &hir.syntax()[1] else { panic!() }; + let ASTNode::Statement(StatementNode::Match { arms, .. }) = &f.body[0] else { panic!() }; let pattern = &arms[0].pattern; let span = hir.pattern_span(hir.pattern_id(pattern).unwrap()).unwrap(); assert_eq!(&source[span.start..span.end], "V::A(x)"); - let MatchPattern::Variant { payloads, .. } = pattern else { - panic!() - }; - let span = hir - .pattern_span(hir.pattern_id(&payloads[0]).unwrap()) - .unwrap(); + let MatchPattern::Variant { payloads, .. } = pattern else { panic!() }; + let span = hir.pattern_span(hir.pattern_id(&payloads[0]).unwrap()).unwrap(); assert_eq!(&source[span.start..span.end], "x"); } @@ -195,9 +148,7 @@ fn generated_expression_depth_is_checked_before_hir_detaches_source_locations() let source = "fun main() -> i32 { return 1; }"; for depth in [128, 129] { let mut syntax = parse(source); - let ASTNode::Function(function) = bare(&mut syntax[0]) else { - panic!() - }; + let ASTNode::Function(function) = bare(&mut syntax[0]) else { panic!() }; let ASTNode::Statement(StatementNode::Return(Some(expression))) = bare(&mut function.body[0]) else { diff --git a/front/hir/tests/typed_hir.rs b/front/hir/tests/typed_hir.rs index 7afeda42..06d8c3cb 100644 --- a/front/hir/tests/typed_hir.rs +++ b/front/hir/tests/typed_hir.rs @@ -37,10 +37,7 @@ fun check(value: u64) -> bool { let Expression::Grouped(arithmetic) = right.as_ref() else { panic!("group"); }; - assert_eq!( - program.type_of(arithmetic), - Some(&HirExpressionType::Resolved(WaveType::Uint(64))) - ); + assert_eq!(program.type_of(arithmetic), Some(&HirExpressionType::Resolved(WaveType::Uint(64)))); let Expression::BinaryExpression { left, right, .. } = arithmetic.as_ref() else { panic!("arithmetic"); }; @@ -77,10 +74,7 @@ fun calculate(left: i64, right: i64) -> i64 { panic!("expected total variable"); }; let binary = total.initial_value.as_ref().expect("expected initializer"); - assert_eq!( - program.type_of(binary), - Some(&HirExpressionType::Resolved(WaveType::Int(64))) - ); + assert_eq!(program.type_of(binary), Some(&HirExpressionType::Resolved(WaveType::Int(64)))); let Expression::BinaryExpression { left, right, .. } = binary else { panic!("expected binary expression"); @@ -124,10 +118,7 @@ fun invalid() -> i32 { let tokens = lexer.tokenize().expect("lex should succeed"); let syntax = parse_syntax_only(&tokens).expect("parse should succeed"); let error = TypedProgram::lower(syntax).expect_err("lowering must reject invalid input"); - assert!(error - .diagnostic() - .message - .contains("undeclared identifier `missing`")); + assert!(error.diagnostic().message.contains("undeclared identifier `missing`")); } #[test] @@ -153,10 +144,7 @@ fun convert(value: Count) -> Status { let ASTNode::Variable(pointer) = &function.body[0] else { panic!("expected pointer variable"); }; - assert_eq!( - pointer.type_name, - WaveType::Pointer(Box::new(WaveType::Int(64))) - ); + assert_eq!(pointer.type_name, WaveType::Pointer(Box::new(WaveType::Int(64)))); let ASTNode::Statement(parser::ast::StatementNode::Return(Some(Expression::Cast { target_type, @@ -179,9 +167,7 @@ fun check(value: u8) { "#, ); program.verify_conversions().unwrap(); - let ASTNode::Function(function) = &program.syntax()[0] else { - panic!() - }; + let ASTNode::Function(function) = &program.syntax()[0] else { panic!() }; let ASTNode::Statement(parser::ast::StatementNode::Expression(Expression::AssignOperation { value, .. @@ -191,33 +177,19 @@ fun check(value: u8) { }; let pointer_type = WaveType::Pointer(Box::new(WaveType::Array(Box::new(WaveType::Uint(64)), 3))); - assert_eq!( - program.numeric_expression_of(value).unwrap().result_type, - pointer_type - ); - let Expression::AddressOf(array) = value.as_ref() else { - panic!() - }; + assert_eq!(program.numeric_expression_of(value).unwrap().result_type, pointer_type); + let Expression::AddressOf(array) = value.as_ref() else { panic!() }; assert_eq!( program.expected_type_of(array), Some(&WaveType::Array(Box::new(WaveType::Uint(64)), 3)) ); - let Expression::ArrayLiteral(elements) = array.as_ref() else { - panic!() - }; + let Expression::ArrayLiteral(elements) = array.as_ref() else { panic!() }; for element in elements { assert_eq!(program.expected_type_of(element), Some(&WaveType::Uint(64))); - assert_eq!( - program.numeric_expression_of(element).unwrap().result_type, - WaveType::Uint(64) - ); + assert_eq!(program.numeric_expression_of(element).unwrap().result_type, WaveType::Uint(64)); } assert_eq!( - program - .numeric_expression_of(&elements[0]) - .unwrap() - .conversions[0] - .kind, + program.numeric_expression_of(&elements[0]).unwrap().conversions[0].kind, hir::conversions::ConversionKind::ZeroExtend ); } diff --git a/front/hir/tests/variant_frontend.rs b/front/hir/tests/variant_frontend.rs index 4f4f4406..49b18bee 100644 --- a/front/hir/tests/variant_frontend.rs +++ b/front/hir/tests/variant_frontend.rs @@ -88,9 +88,7 @@ fun unwrap_bit(item: WrappedBit) -> i32 { .expect("outer variant pattern metadata"); assert_eq!(outer.discriminant, 0); assert_eq!(outer.payload_types, [WaveType::Variant("Cell".into())]); - let MatchPattern::Variant { payloads, .. } = &arms[0].pattern else { - unreachable!() - }; + let MatchPattern::Variant { payloads, .. } = &arms[0].pattern else { unreachable!() }; let nested = program .variant_pattern(program.pattern_id(&payloads[0]).unwrap()) .expect("nested variant pattern metadata"); @@ -125,9 +123,7 @@ fun none() -> Option { }; assert_eq!( program.type_of(expression), - Some(&HirExpressionType::Resolved(WaveType::Variant( - "Option".into() - ))) + Some(&HirExpressionType::Resolved(WaveType::Variant("Option".into()))) ); let construction = program .variant_construction(program.expression_id(expression).unwrap()) @@ -135,11 +131,7 @@ fun none() -> Option { assert_eq!(construction.discriminant, (index - 1) as u32); assert_eq!( construction.payload_types, - if index == 1 { - vec![WaveType::Int(32)] - } else { - Vec::new() - } + if index == 1 { vec![WaveType::Int(32)] } else { Vec::new() } ); } } @@ -170,9 +162,7 @@ fun make() { }; assert_eq!( program.type_of(initializer), - Some(&HirExpressionType::Resolved(WaveType::Variant( - "Outer".into() - ))) + Some(&HirExpressionType::Resolved(WaveType::Variant("Outer".into()))) ); } diff --git a/front/lexer/src/core.rs b/front/lexer/src/core.rs index acb8eacb..e4583784 100644 --- a/front/lexer/src/core.rs +++ b/front/lexer/src/core.rs @@ -29,23 +29,13 @@ pub struct Token { impl Token { pub fn new(token_type: TokenType, lexeme: String, line: usize) -> Self { - Token { - token_type, - lexeme, - line, - span: None, - } + Token { token_type, lexeme, line, span: None } } } impl Default for Token { fn default() -> Self { - Token { - token_type: TokenType::Eof, - lexeme: String::new(), - line: 0, - span: None, - } + Token { token_type: TokenType::Eof, lexeme: String::new(), line: 0, span: None } } } @@ -64,13 +54,7 @@ impl<'a> Lexer<'a> { } pub fn new_with_file(source: &'a str, file: impl Into) -> Lexer<'a> { - Lexer { - source, - file: file.into(), - current: 0, - line: 1, - line_start: 0, - } + Lexer { source, file: file.into(), current: 0, line: 1, line_start: 0 } } pub fn tokenize(&mut self) -> Result, WaveError> { @@ -107,10 +91,8 @@ impl<'a> Lexer<'a> { column: usize, ) -> WaveError { let lines = error::span::source_lines(self.source); - let (line_start, line_text) = lines - .get(line.saturating_sub(1)) - .copied() - .unwrap_or((self.source.len(), "")); + let (line_start, line_text) = + lines.get(line.saturating_sub(1)).copied().unwrap_or((self.source.len(), "")); let start = line_start + line_text .char_indices() diff --git a/front/lexer/src/cursor.rs b/front/lexer/src/cursor.rs index 521caffe..d37f7ee5 100644 --- a/front/lexer/src/cursor.rs +++ b/front/lexer/src/cursor.rs @@ -36,7 +36,7 @@ impl<'a> Lexer<'a> { } else { ('\0', 1) } - } + }, Err(_) => ('\0', 1), }; diff --git a/front/lexer/src/ident.rs b/front/lexer/src/ident.rs index 933ac377..1df6f298 100644 --- a/front/lexer/src/ident.rs +++ b/front/lexer/src/ident.rs @@ -37,17 +37,11 @@ impl<'a> Lexer<'a> { pub(crate) fn keyword_or_ident_token(&self, ident: String) -> Token { match ident.as_str() { - "async" => Token { - token_type: TokenType::Async, - lexeme: ident, - line: self.line, - span: None, + "async" => { + Token { token_type: TokenType::Async, lexeme: ident, line: self.line, span: None } }, - "await" => Token { - token_type: TokenType::Await, - lexeme: ident, - line: self.line, - span: None, + "await" => { + Token { token_type: TokenType::Await, lexeme: ident, line: self.line, span: None } }, "fun" => Token { token_type: TokenType::Fun, diff --git a/front/lexer/src/literals.rs b/front/lexer/src/literals.rs index 51153ac5..4e564e90 100644 --- a/front/lexer/src/literals.rs +++ b/front/lexer/src/literals.rs @@ -124,7 +124,7 @@ impl<'a> Lexer<'a> { .with_help( "valid string byte range: `01` to `FF`; NUL is not allowed", )); - } + }, }; if value == 0 { @@ -139,7 +139,7 @@ impl<'a> Lexer<'a> { .with_help("use a byte array for data containing zero bytes")); } string_literal.push(value); - } + }, _ => { return Err(self .make_error( @@ -154,7 +154,7 @@ impl<'a> Lexer<'a> { .with_code("E1004") .with_label("unsupported escape sequence") .with_help("supported escapes: \\\\, \\\", \\n, \\t, \\r, \\xNN")); - } + }, } } else { if c == '\0' { @@ -287,10 +287,10 @@ impl<'a> Lexer<'a> { ) .with_code("E1005") .with_help("hex escapes must be two hexadecimal digits")); - } + }, }; value as char - } + }, _ => { return Err(self .make_error( @@ -304,7 +304,7 @@ impl<'a> Lexer<'a> { ) .with_code("E1005") .with_help("supported escapes: \\\\, \\\', \\n, \\t, \\r, \\xNN")); - } + }, } } else { self.advance() diff --git a/front/lexer/src/number.rs b/front/lexer/src/number.rs index 2f28b02d..ee2746af 100644 --- a/front/lexer/src/number.rs +++ b/front/lexer/src/number.rs @@ -48,11 +48,7 @@ impl IntegerLiteral { Some(b"0b" | b"0B") => (2, &unsigned[2..]), _ => (10, unsigned), }; - Some(Self { - negative, - radix, - digits: digits(raw_digits, radix)?, - }) + Some(Self { negative, radix, digits: digits(raw_digits, radix)? }) } pub fn is_zero(&self) -> bool { @@ -150,18 +146,13 @@ impl IntegerLiteral { } let text: String = decimal.iter().rev().map(|d| char::from(b'0' + d)).collect(); let value: f64 = text.parse().ok()?; - value - .is_finite() - .then_some(if self.negative { -value } else { value }) + value.is_finite().then_some(if self.negative { -value } else { value }) } } /// Parse a finite decimal float, validating separators before normalization. pub fn parse_float(raw: &str) -> Option { - let unsigned = raw - .strip_prefix('-') - .or_else(|| raw.strip_prefix('+')) - .unwrap_or(raw); + let unsigned = raw.strip_prefix('-').or_else(|| raw.strip_prefix('+')).unwrap_or(raw); let mut exponent_parts = unsigned.split(['e', 'E']); let mantissa = exponent_parts.next()?; let exponent = exponent_parts.next(); @@ -169,12 +160,7 @@ pub fn parse_float(raw: &str) -> Option { return None; } if let Some(exp) = exponent { - digits( - exp.strip_prefix('-') - .or_else(|| exp.strip_prefix('+')) - .unwrap_or(exp), - 10, - )?; + digits(exp.strip_prefix('-').or_else(|| exp.strip_prefix('+')).unwrap_or(exp), 10)?; } let mut fraction_parts = mantissa.split('.'); digits(fraction_parts.next()?, 10)?; diff --git a/front/lexer/src/scan.rs b/front/lexer/src/scan.rs index dd90a85e..1ee643ff 100644 --- a/front/lexer/src/scan.rs +++ b/front/lexer/src/scan.rs @@ -83,7 +83,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '-' => { if self.match_next('-') { return Ok(Token { @@ -114,7 +114,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '*' => { if self.match_next('=') { return Ok(Token { @@ -131,7 +131,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '.' => { return Ok(Token { token_type: TokenType::Dot, @@ -139,7 +139,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '/' => { if self.match_next('=') { return Ok(Token { @@ -156,7 +156,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '%' => { if self.match_next('=') { return Ok(Token { @@ -173,7 +173,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, ';' => { return Ok(Token { token_type: TokenType::SemiColon, @@ -181,7 +181,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, ':' => { if self.match_next(':') { return Ok(Token { @@ -197,7 +197,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }); - } + }, '<' => { if self.match_next('<') { return Ok(Token { @@ -221,7 +221,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '>' => { if self.match_next('>') { return Ok(Token { @@ -245,7 +245,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '(' => { return Ok(Token { token_type: TokenType::Lparen, @@ -253,7 +253,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, ')' => { return Ok(Token { token_type: TokenType::Rparen, @@ -261,7 +261,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '{' => { return Ok(Token { token_type: TokenType::Lbrace, @@ -269,7 +269,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '}' => { return Ok(Token { token_type: TokenType::Rbrace, @@ -277,7 +277,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '[' => { return Ok(Token { token_type: TokenType::Lbrack, @@ -285,7 +285,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, ']' => { return Ok(Token { token_type: TokenType::Rbrack, @@ -293,7 +293,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '=' => { if self.match_next('=') { return Ok(Token { @@ -310,7 +310,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '&' => { if self.match_next('&') { return Ok(Token { @@ -327,7 +327,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '|' => { if self.match_next('|') { return Ok(Token { @@ -344,7 +344,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '!' => { if self.match_next('=') { return Ok(Token { @@ -375,7 +375,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '^' => { return Ok(Token { token_type: TokenType::Xor, @@ -383,7 +383,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '~' => { if self.match_next('^') { return Ok(Token { @@ -400,7 +400,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, '?' => { if self.match_next('?') { return Ok(Token { @@ -417,7 +417,7 @@ impl<'a> Lexer<'a> { span: None, }); } - } + }, ',' => { return Ok(Token { token_type: TokenType::Comma, @@ -425,7 +425,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }) - } + }, '\'' => { let value = self.char_literal()?; return Ok(Token { @@ -434,7 +434,7 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }); - } + }, '"' => { let string_value = self.string()?; return Ok(Token { @@ -443,12 +443,12 @@ impl<'a> Lexer<'a> { line: self.line, span: None, }); - } + }, ch if ch.is_alphabetic() || ch == '_' => { let ident = self.identifier(c); return Ok(self.keyword_or_ident_token(ident)); - } + }, '0'..='9' => { let start = self.current - 1; @@ -475,7 +475,7 @@ impl<'a> Lexer<'a> { WaveErrorKind::InvalidNumber(raw.to_string()), format!("invalid numeric literal `{raw}`"), self.line, self.column_at(start), ).with_code("E1006").with_help("use binary, octal, decimal or hexadecimal digits; separate digits with a single underscore; floats use decimal fractions or exponents, without suffixes")); - } + }, _ => { let (line, column) = (self.line, self.column_at(self.current - c.len_utf8())); @@ -515,7 +515,7 @@ impl<'a> Lexer<'a> { .with_label("this character is not valid in Wave syntax") .with_help("remove it or replace it with a valid token")); } - } + }, } } } diff --git a/front/lexer/src/trivia.rs b/front/lexer/src/trivia.rs index 5b8f345e..48dd5b11 100644 --- a/front/lexer/src/trivia.rs +++ b/front/lexer/src/trivia.rs @@ -66,7 +66,7 @@ impl<'a> Lexer<'a> { match c { ' ' | '\t' => { self.advance(); - } + }, '\n' | '\r' => self.consume_newline(), _ => break, } diff --git a/front/lexer/tests/literals_and_newlines.rs b/front/lexer/tests/literals_and_newlines.rs index f7004b9b..72ea8286 100644 --- a/front/lexer/tests/literals_and_newlines.rs +++ b/front/lexer/tests/literals_and_newlines.rs @@ -3,31 +3,17 @@ use lexer::{token::TokenType, Lexer}; #[test] fn every_source_newline_has_the_same_tokens_and_logical_positions() { for newline in ["\n", "\r\n", "\r"] { - let source = [ - "// comment", - "/* outer", - "/* nested */ ordinary", - "*/", - " 이름", - " + 1", - ] - .join(newline); - let tokens = Lexer::new_with_file(&source, "lines.wave") - .tokenize() - .unwrap(); + let source = ["// comment", "/* outer", "/* nested */ ordinary", "*/", " 이름", " + 1"] + .join(newline); + let tokens = Lexer::new_with_file(&source, "lines.wave").tokenize().unwrap(); assert_eq!(tokens[0].lexeme, "이름"); assert_eq!(tokens[0].line, 5); assert_eq!(tokens[0].span.as_ref().unwrap().column, 3); - assert_eq!( - tokens[0].span.as_ref().unwrap().start, - source.find("이름").unwrap() - ); + assert_eq!(tokens[0].span.as_ref().unwrap().start, source.find("이름").unwrap()); assert_eq!(tokens[1].line, 6); assert_eq!(tokens[1].span.as_ref().unwrap().column, 3); let bad = format!("// text{newline} \"한\\q\""); - let error = Lexer::new_with_file(&bad, "lines.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file(&bad, "lines.wave").tokenize().unwrap_err(); assert_eq!(error.code.as_deref(), Some("E1004")); assert_eq!((error.line, error.column), (2, 5)); assert_eq!( @@ -36,13 +22,8 @@ fn every_source_newline_has_the_same_tokens_and_logical_positions() { ); for quote in ['\"', '\''] { let bad = format!("{quote}{newline}{quote}"); - let error = Lexer::new_with_file(&bad, "lines.wave") - .tokenize() - .unwrap_err(); - assert_eq!( - error.code.as_deref(), - Some(if quote == '\"' { "E1003" } else { "E1005" }) - ); + let error = Lexer::new_with_file(&bad, "lines.wave").tokenize().unwrap_err(); + assert_eq!(error.code.as_deref(), Some(if quote == '\"' { "E1003" } else { "E1005" })); assert_eq!((error.line, error.column), (1, 1)); } } @@ -50,14 +31,10 @@ fn every_source_newline_has_the_same_tokens_and_logical_positions() { #[test] fn nested_and_unterminated_comments_have_stable_diagnostics() { - let tokens = Lexer::new("/* a /* b /* c */ b */ a */ value // tail") - .tokenize() - .unwrap(); + let tokens = Lexer::new("/* a /* b /* c */ b */ a */ value // tail").tokenize().unwrap(); assert_eq!(tokens[0].lexeme, "value"); assert_eq!(tokens[0].line, 1); - let error = Lexer::new_with_file("/* a\n /* b */", "comment.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file("/* a\n /* b */", "comment.wave").tokenize().unwrap_err(); assert_eq!(error.code.as_deref(), Some("E1002")); assert_eq!(error.line, 2); assert_eq!(error.span.as_ref().unwrap().file, "comment.wave"); @@ -66,16 +43,12 @@ fn nested_and_unterminated_comments_have_stable_diagnostics() { #[test] fn string_bytes_preserve_hex_and_utf8_without_an_embedded_nul() { - let tokens = Lexer::new(r#""한\x7F\x80\xFF\n\t\r\\\"""#) - .tokenize() - .unwrap(); + let tokens = Lexer::new(r#""한\x7F\x80\xFF\n\t\r\\\"""#).tokenize().unwrap(); let mut expected = "한".as_bytes().to_vec(); expected.extend_from_slice(&[0x7f, 0x80, 0xff, b'\n', b'\t', b'\r', b'\\', b'"']); assert_eq!(tokens[0].token_type, TokenType::String(expected)); for (source, column, fragment) in [(r#""\x00""#, 2, "\\x00"), ("\"한\0b\"", 3, "\0")] { - let error = Lexer::new_with_file(source, "nul.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file(source, "nul.wave").tokenize().unwrap_err(); assert_eq!(error.code.as_deref(), Some("E1004")); assert!(error.message.contains("NUL")); assert_eq!((error.line, error.column), (1, column)); @@ -112,9 +85,7 @@ fn valid_and_invalid_character_and_string_escapes_retain_locations() { ("'한'", "E1005", "unsigned 8-bit"), ] { let source = format!("\n {source}"); - let error = Lexer::new_with_file(&source, "literal.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file(&source, "literal.wave").tokenize().unwrap_err(); assert_eq!(error.code.as_deref(), Some(code), "{source}: {error:?}"); assert!(error.message.contains(message), "{source}: {error:?}"); assert_eq!(error.line, 2); diff --git a/front/lexer/tests/numeric_contract.rs b/front/lexer/tests/numeric_contract.rs index 790748ca..57ecf36a 100644 --- a/front/lexer/tests/numeric_contract.rs +++ b/front/lexer/tests/numeric_contract.rs @@ -17,29 +17,19 @@ fn numeric_tokens_and_shared_values_agree() { let number = IntegerLiteral::parse(raw).unwrap(); assert_eq!(number.to_i128(), Some(value)); let tokens = Lexer::new(raw).tokenize().unwrap(); - let TokenType::IntLiteral(normalized) = &tokens[0].token_type else { - panic!() - }; + let TokenType::IntLiteral(normalized) = &tokens[0].token_type else { panic!() }; assert_eq!(IntegerLiteral::parse(normalized), Some(number)); } assert_eq!( - IntegerLiteral::parse("-170141183460469231731687303715884105728") - .unwrap() - .to_i128(), + IntegerLiteral::parse("-170141183460469231731687303715884105728").unwrap().to_i128(), Some(i128::MIN) ); assert_eq!( - IntegerLiteral::parse("-9223372036854775808") - .unwrap() - .to_f64(), + IntegerLiteral::parse("-9223372036854775808").unwrap().to_f64(), Some(i64::MIN as f64) ); - for (raw, value) in [ - ("1.0", 1.0), - ("1e3", 1000.0), - ("1E-3", 0.001), - ("1_000.2_5e+2", 100025.0), - ] { + for (raw, value) in [("1.0", 1.0), ("1e3", 1000.0), ("1E-3", 0.001), ("1_000.2_5e+2", 100025.0)] + { assert_eq!(parse_float(raw), Some(value)); assert!( matches!(Lexer::new(raw).tokenize().unwrap()[0].token_type,TokenType::Float(n) if n == value) diff --git a/front/lexer/tests/string_escape_spans.rs b/front/lexer/tests/string_escape_spans.rs index bbd966ff..97588e5f 100644 --- a/front/lexer/tests/string_escape_spans.rs +++ b/front/lexer/tests/string_escape_spans.rs @@ -17,27 +17,15 @@ fn invalid_string_escapes_preserve_exact_utf8_ranges() { ] { for prefix in ["\"", "// 한글\r\nfun main() { \"앞"] { let source = format!("{prefix}{escape}{suffix}"); - let error = Lexer::new_with_file(&source, "escape.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file(&source, "escape.wave").tokenize().unwrap_err(); assert_eq!(error.code.as_deref(), Some("E1004"), "{error:?}"); let span = error.span.as_ref().unwrap(); assert_eq!(span.file, "escape.wave"); assert_eq!(span.start, prefix.len(), "{source:?}: {error:?}"); - assert_eq!( - span.end, - prefix.len() + escape.len(), - "{source:?}: {error:?}" - ); + assert_eq!(span.end, prefix.len() + escape.len(), "{source:?}: {error:?}"); assert_eq!(&source[span.start..span.end], escape); - assert_eq!( - span.line, - prefix.bytes().filter(|b| *b == b'\n').count() + 1 - ); - assert_eq!( - span.column, - prefix.rsplit('\n').next().unwrap().chars().count() + 1 - ); + assert_eq!(span.line, prefix.bytes().filter(|b| *b == b'\n').count() + 1); + assert_eq!(span.column, prefix.rsplit('\n').next().unwrap().chars().count() + 1); assert_eq!(error.column, span.column); assert_eq!(error.span_len, escape.chars().count()); } @@ -48,8 +36,5 @@ fn invalid_string_escapes_preserve_exact_utf8_ranges() { fn supported_string_escapes_keep_their_values() { let source = r#""앞\n\t\r\\\"\x41""#; let tokens = Lexer::new(source).tokenize().unwrap(); - assert_eq!( - tokens[0].token_type, - TokenType::String("앞\n\t\r\\\"A".into()) - ); + assert_eq!(tokens[0].token_type, TokenType::String("앞\n\t\r\\\"A".into())); } diff --git a/front/lexer/tests/token_contract.rs b/front/lexer/tests/token_contract.rs index ac0003b1..8e65b40c 100644 --- a/front/lexer/tests/token_contract.rs +++ b/front/lexer/tests/token_contract.rs @@ -166,26 +166,15 @@ fn operators_take_the_longest_token_even_at_eof() { ] { let mut tokens = Lexer::new(source).tokenize().unwrap(); tokens.pop(); - assert_eq!( - tokens - .iter() - .map(|t| t.token_type.clone()) - .collect::>(), - expected - ); - assert_eq!( - tokens.iter().map(|t| t.lexeme.as_str()).collect::(), - source - ); + assert_eq!(tokens.iter().map(|t| t.token_type.clone()).collect::>(), expected); + assert_eq!(tokens.iter().map(|t| t.lexeme.as_str()).collect::(), source); } } #[test] fn unicode_identifiers_and_lookahead_preserve_spelling_and_character_columns() { let source = "이름+=é;\n变量!=\"값\";"; - let tokens = Lexer::new_with_file(source, "unicode.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "unicode.wave").tokenize().unwrap(); for (index, spelling, line, column) in [ (0, "이름", 1, 1), (1, "+=", 1, 3), @@ -198,14 +187,9 @@ fn unicode_identifiers_and_lookahead_preserve_spelling_and_character_columns() { assert_eq!(t.lexeme, spelling); assert_eq!(t.line, line); assert_eq!(t.span.as_ref().unwrap().column, column); - assert_eq!( - &source[t.span.as_ref().unwrap().start..t.span.as_ref().unwrap().end], - spelling - ); + assert_eq!(&source[t.span.as_ref().unwrap().start..t.span.as_ref().unwrap().end], spelling); } - let error = Lexer::new_with_file("é한 @", "unicode.wave") - .tokenize() - .unwrap_err(); + let error = Lexer::new_with_file("é한 @", "unicode.wave").tokenize().unwrap_err(); assert_eq!(error.line, 1); assert_eq!(error.column, 4); assert_eq!(error.code.as_deref(), Some("E1001")); diff --git a/front/parser/src/ast.rs b/front/parser/src/ast.rs index 996ade34..0ca736a4 100644 --- a/front/parser/src/ast.rs +++ b/front/parser/src/ast.rs @@ -44,10 +44,7 @@ pub enum WaveType { #[derive(Debug, Clone)] pub enum ASTNode { - Located { - value: Box, - span: error::SourceSpan, - }, + Located { value: Box, span: error::SourceSpan }, Function(FunctionNode), ExternFunction(ExternFunctionNode), Program(ParameterNode), @@ -301,19 +298,12 @@ pub enum AssignOperator { #[derive(Debug, Clone)] pub enum MatchPattern { - Located { - value: Box, - span: error::SourceSpan, - }, + Located { value: Box, span: error::SourceSpan }, Int(String), Ident(String), Binding(String), Wildcard, - Variant { - variant_type: String, - case_name: String, - payloads: Vec, - }, + Variant { variant_type: String, case_name: String, payloads: Vec }, } #[derive(Debug, Clone)] @@ -415,15 +405,10 @@ impl Expression { match self.unspanned() { Self::Literal(Literal::Int(_)) => true, Self::Grouped(inner) => inner.is_contextual_integer(), - Self::Unary { - operator: Operator::Neg | Operator::BitwiseNot, - expr, - } => expr.is_contextual_integer(), - Self::BinaryExpression { - left, - operator, - right, - } => { + Self::Unary { operator: Operator::Neg | Operator::BitwiseNot, expr } => { + expr.is_contextual_integer() + }, + Self::BinaryExpression { left, operator, right } => { matches!( operator, Operator::Add @@ -438,7 +423,7 @@ impl Expression { | Operator::BitwiseXor ) && left.is_contextual_integer() && right.is_contextual_integer() - } + }, _ => false, } } @@ -452,7 +437,7 @@ impl Expression { } else { None } - } + }, _ => None, } } @@ -465,27 +450,27 @@ impl ASTNode { other => other, } } + pub fn into_unspanned(self) -> Self { match self { Self::Located { value, .. } => value.into_unspanned(), other => other, } } + pub fn span(&self) -> Option<&error::SourceSpan> { match self { Self::Located { span, .. } => Some(span), _ => None, } } + pub fn with_span(self, span: Option) -> Self { if self.span() == span.as_ref() { return self; } match span { - Some(span) => Self::Located { - value: Box::new(self), - span, - }, + Some(span) => Self::Located { value: Box::new(self), span }, None => self, } } @@ -498,27 +483,27 @@ impl Expression { other => other, } } + pub fn into_unspanned(self) -> Self { match self { Self::Located { value, .. } => value.into_unspanned(), other => other, } } + pub fn span(&self) -> Option<&error::SourceSpan> { match self { Self::Located { span, .. } => Some(span), _ => None, } } + pub fn with_span(self, span: Option) -> Self { if self.span() == span.as_ref() { return self; } match span { - Some(span) => Self::Located { - value: Box::new(self), - span, - }, + Some(span) => Self::Located { value: Box::new(self), span }, None => self, } } @@ -531,27 +516,27 @@ impl MatchPattern { other => other, } } + pub fn into_unspanned(self) -> Self { match self { Self::Located { value, .. } => value.into_unspanned(), other => other, } } + pub fn span(&self) -> Option<&error::SourceSpan> { match self { Self::Located { span, .. } => Some(span), _ => None, } } + pub fn with_span(self, span: Option) -> Self { if self.span() == span.as_ref() { return self; } match span { - Some(span) => Self::Located { - value: Box::new(self), - span, - }, + Some(span) => Self::Located { value: Box::new(self), span }, None => self, } } @@ -559,16 +544,9 @@ impl MatchPattern { impl Expression { pub fn binary(left: Expression, operator: Operator, right: Expression) -> Self { - let span = left - .span() - .zip(right.span()) - .map(|(first, last)| first.through(last)); - Self::BinaryExpression { - left: Box::new(left), - operator, - right: Box::new(right), - } - .with_span(span) + let span = left.span().zip(right.span()).map(|(first, last)| first.through(last)); + Self::BinaryExpression { left: Box::new(left), operator, right: Box::new(right) } + .with_span(span) } } diff --git a/front/parser/src/ast/visit.rs b/front/parser/src/ast/visit.rs index 52088464..441eea71 100644 --- a/front/parser/src/ast/visit.rs +++ b/front/parser/src/ast/visit.rs @@ -29,7 +29,7 @@ pub fn walk_node(node: &ASTNode, visit: &mut impl FnMut(&Expression)) { } } walk_nodes(&function.body, visit); - } + }, ASTNode::Struct(structure) => { for method in &structure.methods { for parameter in &method.parameters { @@ -39,7 +39,7 @@ pub fn walk_node(node: &ASTNode, visit: &mut impl FnMut(&Expression)) { } walk_nodes(&method.body, visit); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { for parameter in &method.parameters { @@ -49,19 +49,19 @@ pub fn walk_node(node: &ASTNode, visit: &mut impl FnMut(&Expression)) { } walk_nodes(&method.body, visit); } - } + }, ASTNode::Statement(statement) => walk_statement(statement, visit), ASTNode::Variable(variable) => { if let Some(initializer) = &variable.initial_value { walk_expression(initializer, visit); } - } + }, ASTNode::Expression(expression) => walk_expression(expression, visit), ASTNode::ExternFunction(_) | ASTNode::Program(_) | ASTNode::TypeAlias(_) | ASTNode::Enum(_) - | ASTNode::Variant(_) => {} + | ASTNode::Variant(_) => {}, } } @@ -73,13 +73,8 @@ fn walk_statement(statement: &StatementNode, visit: &mut impl FnMut(&Expression) for argument in args { walk_expression(argument, visit); } - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { walk_expression(condition, visit); walk_nodes(body, visit); if let Some(blocks) = else_if_blocks { @@ -91,46 +86,39 @@ fn walk_statement(statement: &StatementNode, visit: &mut impl FnMut(&Expression) if let Some(body) = else_block { walk_nodes(body, visit); } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + }, + StatementNode::For { initialization, condition, increment, body } => { walk_node(initialization, visit); walk_expression(condition, visit); walk_expression(increment, visit); walk_nodes(body, visit); - } + }, StatementNode::While { condition, body } => { walk_expression(condition, visit); walk_nodes(body, visit); - } + }, StatementNode::Match { value, arms } => { walk_expression(value, visit); for arm in arms { walk_nodes(&arm.body, visit); } - } + }, StatementNode::Assign { value, .. } => walk_expression(value, visit), - StatementNode::AsmBlock { - inputs, outputs, .. - } => { + StatementNode::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter().chain(outputs.iter()) { walk_expression(expression, visit); } - } + }, StatementNode::Return(Some(expression)) | StatementNode::Expression(expression) => { walk_expression(expression, visit) - } + }, StatementNode::Print(_) | StatementNode::Println(_) | StatementNode::Variable(_) | StatementNode::Import(_) | StatementNode::Break | StatementNode::Continue - | StatementNode::Return(None) => {} + | StatementNode::Return(None) => {}, } } @@ -154,18 +142,18 @@ pub fn walk_expression_children<'a>( for (_, value) in fields { visit(value); } - } + }, Expression::FunctionCall { args, .. } => { for argument in args { visit(argument); } - } + }, Expression::MethodCall { object, args, .. } => { visit(object); for argument in args { visit(argument); } - } + }, Expression::Deref(inner) | Expression::AddressOf(inner) | Expression::Await(inner) @@ -175,34 +163,22 @@ pub fn walk_expression_children<'a>( | Expression::FieldAccess { object: inner, .. } | Expression::IncDec { target: inner, .. } => visit(inner), Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } - | Expression::AssignOperation { - target: left, - value: right, - .. - } - | Expression::Assignment { - target: left, - value: right, - } => { + | Expression::IndexAccess { target: left, index: right } + | Expression::AssignOperation { target: left, value: right, .. } + | Expression::Assignment { target: left, value: right } => { visit(left); visit(right); - } + }, Expression::ArrayLiteral(values) => { for value in values { visit(value); } - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter().chain(outputs.iter()) { visit(expression); } - } - Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {} + }, + Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {}, } } diff --git a/front/parser/src/ast_output.rs b/front/parser/src/ast_output.rs index 0c4c4916..2eb955b2 100644 --- a/front/parser/src/ast_output.rs +++ b/front/parser/src/ast_output.rs @@ -34,6 +34,7 @@ fn record(kind: &str, fields: Vec<(&str, Value)>) -> Value { result.extend(fields.into_iter().map(|(k, v)| (k.into(), v))); Value::Object(result) } + fn put(value: &mut Value, key: &str, item: Value) { if let Value::Object(fields) = value { if let Some((_, existing)) = fields.iter_mut().find(|(k, _)| k == key) { @@ -106,10 +107,7 @@ pub fn dump( line: e.span.as_ref().map_or(1, |s| s.line), column: e.span.as_ref().map_or(1, |s| s.column), })?; - let cx = Context { - source, - expressions: None, - }; + let cx = Context { source, expressions: None }; let value = Value::Object(vec![ ("schema_version".into(), Value::integer(1)), ("stage".into(), Value::String("parsed".into())), @@ -120,10 +118,7 @@ pub fn dump( ("path", Value::string(file)), ])]), ), - ( - "nodes".into(), - Value::Array(nodes.iter().map(|node| node.data(&cx)).collect()), - ), + ("nodes".into(), Value::Array(nodes.iter().map(|node| node.data(&cx)).collect())), ]); let text = match format { AstFormat::Wson => wson::dumps_with_depth_limit(&value, wson::Format::Wson, true, 512)?, @@ -131,17 +126,16 @@ pub fn dump( AstFormat::Sexpr => { wson::dumps_with_depth_limit(&value, wson::Format::Json, false, 512)?; sexpr(&value, 0) - } + }, }; Ok(text + "\n") } + fn sexpr(value: &Value, depth: usize) -> String { let indent = " ".repeat(depth + 1); match value { Value::Object(fields) => { - let tag = value - .get_str("kind") - .unwrap_or(if depth == 0 { "ast" } else { "record" }); + let tag = value.get_str("kind").unwrap_or(if depth == 0 { "ast" } else { "record" }); let mut out = format!("({tag}"); for (key, v) in fields { if key != "kind" { @@ -150,7 +144,7 @@ fn sexpr(value: &Value, depth: usize) -> String { } out.push(')'); out - } + }, Value::Array(values) => { let mut out = String::from("(list"); for item in values { @@ -158,7 +152,7 @@ fn sexpr(value: &Value, depth: usize) -> String { } out.push(')'); out - } + }, Value::String(s) => wson::quote(s), Value::Number(n) => n.as_str().into(), Value::Bool(b) => b.to_string(), @@ -181,10 +175,9 @@ impl Data for WaveType { Self::String => record("string_type", vec![]), Self::Pointer(p0) => record("pointer_type", vec![("pointee", p0.data(cx))]), Self::Future(p0) => record("future_type", vec![("result", p0.data(cx))]), - Self::Array(p0, p1) => record( - "array_type", - vec![("element", p0.data(cx)), ("length", p1.data(cx))], - ), + Self::Array(p0, p1) => { + record("array_type", vec![("element", p0.data(cx)), ("length", p1.data(cx))]) + }, Self::Void => record("void_type", vec![]), Self::Never => record("never_type", vec![]), Self::Struct(p0) => record("struct_type", vec![("name", p0.data(cx))]), @@ -199,7 +192,7 @@ impl Data for ASTNode { let mut node = value.data(cx); put(&mut node, "span", span.data(cx)); node - } + }, Self::Function(p0) => p0.data(cx), Self::ExternFunction(p0) => p0.data(cx), Self::Program(p0) => p0.data(cx), @@ -330,10 +323,7 @@ impl Data for ProtoImplNode { fn data(&self, cx: &Context) -> Value { record( "proto_impl", - vec![ - ("target", self.target.data(cx)), - ("methods", self.methods.data(cx)), - ], + vec![("target", self.target.data(cx)), ("methods", self.methods.data(cx))], ) } } @@ -406,17 +396,12 @@ impl Data for Expression { // Convert bottom-up: a bounded source tree must not multiply native // stack frames for location wrappers and wire-format containers. let values = RefCell::new(HashMap::new()); - let nested = Context { - source: cx.source, - expressions: Some(&values), - }; + let nested = Context { source: cx.source, expressions: Some(&values) }; let mut pending = vec![(self, false)]; while let Some((expression, ready)) = pending.pop() { if ready { let value = expression_record(expression, &nested); - values - .borrow_mut() - .insert(expression as *const Expression, value); + values.borrow_mut().insert(expression as *const Expression, value); } else { pending.push((expression, true)); crate::ast::visit::walk_expression_children(expression, &mut |child| { @@ -443,16 +428,11 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { } } node - } - Expression::StructLiteral { name, fields } => record( - "struct_literal", - vec![("name", name.data(cx)), ("fields", fields.data(cx))], - ), - Expression::FunctionCall { - name, - type_args, - args, - } => record( + }, + Expression::StructLiteral { name, fields } => { + record("struct_literal", vec![("name", name.data(cx)), ("fields", fields.data(cx))]) + }, + Expression::FunctionCall { name, type_args, args } => record( "function_call", vec![ ("name", name.data(cx)), @@ -460,12 +440,7 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { ("args", args.data(cx)), ], ), - Expression::MethodCall { - object, - name, - type_args, - args, - } => record( + Expression::MethodCall { object, name, type_args, args } => record( "method_call", vec![ ("object", object.data(cx)), @@ -480,11 +455,7 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { Expression::Variable(p0) => record("variable", vec![("name", p0.data(cx))]), Expression::Deref(p0) => record("deref", vec![("operand", p0.data(cx))]), Expression::AddressOf(p0) => record("address_of", vec![("operand", p0.data(cx))]), - Expression::BinaryExpression { - left, - operator, - right, - } => record( + Expression::BinaryExpression { left, operator, right } => record( "binary_expression", vec![ ("left", left.data(cx)), @@ -492,17 +463,12 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { ("right", right.data(cx)), ], ), - Expression::IndexAccess { target, index } => record( - "index_access", - vec![("target", target.data(cx)), ("index", index.data(cx))], - ), + Expression::IndexAccess { target, index } => { + record("index_access", vec![("target", target.data(cx)), ("index", index.data(cx))]) + }, Expression::ArrayLiteral(p0) => record("array_literal", vec![("elements", p0.data(cx))]), Expression::Grouped(p0) => record("grouped", vec![("expression", p0.data(cx))]), - Expression::AssignOperation { - target, - operator, - value, - } => record( + Expression::AssignOperation { target, operator, value } => record( "assign_operation", vec![ ("target", target.data(cx)), @@ -510,16 +476,10 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { ("value", value.data(cx)), ], ), - Expression::Assignment { target, value } => record( - "assignment", - vec![("target", target.data(cx)), ("value", value.data(cx))], - ), - Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => record( + Expression::Assignment { target, value } => { + record("assignment", vec![("target", target.data(cx)), ("value", value.data(cx))]) + }, + Expression::AsmBlock { instructions, inputs, outputs, clobbers } => record( "asm_block", vec![ ("instructions", instructions.data(cx)), @@ -528,25 +488,18 @@ fn expression_record(expression: &Expression, cx: &Context) -> Value { ("clobbers", clobbers.data(cx)), ], ), - Expression::FieldAccess { object, field } => record( - "field_access", - vec![("object", object.data(cx)), ("field", field.data(cx))], - ), - Expression::Unary { operator, expr } => record( - "unary", - vec![("operator", operator.data(cx)), ("expr", expr.data(cx))], - ), - Expression::Cast { expr, target_type } => record( - "cast", - vec![ - ("expr", expr.data(cx)), - ("target_type", target_type.data(cx)), - ], - ), - Expression::IncDec { kind, target } => record( - "inc_dec", - vec![("kind", kind.data(cx)), ("target", target.data(cx))], - ), + Expression::FieldAccess { object, field } => { + record("field_access", vec![("object", object.data(cx)), ("field", field.data(cx))]) + }, + Expression::Unary { operator, expr } => { + record("unary", vec![("operator", operator.data(cx)), ("expr", expr.data(cx))]) + }, + Expression::Cast { expr, target_type } => { + record("cast", vec![("expr", expr.data(cx)), ("target_type", target_type.data(cx))]) + }, + Expression::IncDec { kind, target } => { + record("inc_dec", vec![("kind", kind.data(cx)), ("target", target.data(cx))]) + }, } } @@ -610,16 +563,12 @@ impl Data for MatchPattern { let mut node = value.data(cx); put(&mut node, "span", span.data(cx)); node - } + }, Self::Int(p0) => record("int", vec![("value", p0.data(cx))]), Self::Ident(p0) => record("ident", vec![("name", p0.data(cx))]), Self::Binding(p0) => record("binding", vec![("name", p0.data(cx))]), Self::Wildcard => record("wildcard", vec![]), - Self::Variant { - variant_type, - case_name, - payloads, - } => record( + Self::Variant { variant_type, case_name, payloads } => record( "variant", vec![ ("variant_type", variant_type.data(cx)), @@ -646,26 +595,18 @@ impl Data for StatementNode { fn data(&self, cx: &Context) -> Value { match self { Self::Print(p0) => record("print", vec![("bytes", p0.data(cx))]), - Self::PrintFormat { format, args } => record( - "print_format", - vec![("format", format.data(cx)), ("args", args.data(cx))], - ), + Self::PrintFormat { format, args } => { + record("print_format", vec![("format", format.data(cx)), ("args", args.data(cx))]) + }, Self::Println(p0) => record("println", vec![("bytes", p0.data(cx))]), - Self::PrintlnFormat { format, args } => record( - "println_format", - vec![("format", format.data(cx)), ("args", args.data(cx))], - ), - Self::Input { format, args } => record( - "input", - vec![("format", format.data(cx)), ("args", args.data(cx))], - ), + Self::PrintlnFormat { format, args } => { + record("println_format", vec![("format", format.data(cx)), ("args", args.data(cx))]) + }, + Self::Input { format, args } => { + record("input", vec![("format", format.data(cx)), ("args", args.data(cx))]) + }, Self::Variable(p0) => record("variable", vec![("name", p0.data(cx))]), - Self::If { - condition, - body, - else_if_blocks, - else_block, - } => record( + Self::If { condition, body, else_if_blocks, else_block } => record( "if", vec![ ("condition", condition.data(cx)), @@ -674,12 +615,7 @@ impl Data for StatementNode { ("else_block", else_block.data(cx)), ], ), - Self::For { - initialization, - condition, - increment, - body, - } => record( + Self::For { initialization, condition, increment, body } => record( "for", vec![ ("initialization", initialization.data(cx)), @@ -688,25 +624,17 @@ impl Data for StatementNode { ("body", body.data(cx)), ], ), - Self::While { condition, body } => record( - "while", - vec![("condition", condition.data(cx)), ("body", body.data(cx))], - ), - Self::Match { value, arms } => record( - "match", - vec![("value", value.data(cx)), ("arms", arms.data(cx))], - ), + Self::While { condition, body } => { + record("while", vec![("condition", condition.data(cx)), ("body", body.data(cx))]) + }, + Self::Match { value, arms } => { + record("match", vec![("value", value.data(cx)), ("arms", arms.data(cx))]) + }, Self::Import(p0) => p0.data(cx), - Self::Assign { variable, value } => record( - "assign", - vec![("variable", variable.data(cx)), ("value", value.data(cx))], - ), - Self::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => record( + Self::Assign { variable, value } => { + record("assign", vec![("variable", variable.data(cx)), ("value", value.data(cx))]) + }, + Self::AsmBlock { instructions, inputs, outputs, clobbers } => record( "asm_block", vec![ ("instructions", instructions.data(cx)), diff --git a/front/parser/src/async_intrinsics.rs b/front/parser/src/async_intrinsics.rs index d1010a3b..ca8c9b32 100644 --- a/front/parser/src/async_intrinsics.rs +++ b/front/parser/src/async_intrinsics.rs @@ -37,13 +37,7 @@ pub fn signature( let scalar = || { arguments .last() - .and_then(|t| { - if let Future(t) = t { - Some(*t.clone()) - } else { - None - } - }) + .and_then(|t| if let Future(t) = t { Some(*t.clone()) } else { None }) .ok_or_else(|| format!("{name} requires a Future")) }; let no_types = || { @@ -58,11 +52,7 @@ pub fn signature( "__wave_async_create" if types.len() == 2 => ( vec![ ptr(types[0].clone()), - ptr(if types[1] == Void { - Byte - } else { - types[1].clone() - }), + ptr(if types[1] == Void { Byte } else { types[1].clone() }), String, ], future(types[1].clone()), @@ -70,71 +60,68 @@ pub fn signature( "__wave_async_complete" => { no_types()?; (vec![Int(64)], Void) - } + }, "__wave_async_wait" => { no_types()?; let t = scalar()?; (vec![Int(64), future(t)], Void) - } + }, "__wave_async_ready" | "__wave_async_cancel" => { no_types()?; let t = scalar()?; (vec![future(t)], Bool) - } + }, "__wave_async_take" | "__wave_async_block_on" => { no_types()?; let t = scalar()?; (vec![future(t.clone())], t) - } + }, "__wave_async_spawn" => { no_types()?; let t = scalar()?; (vec![future(t.clone())], future(t)) - } + }, "__wave_async_yield" => { no_types()?; (vec![], future(Void)) - } + }, "__wave_async_shutdown" => { no_types()?; (vec![], Void) - } + }, "__wave_async_free_slot" if arguments.len() == 1 && matches!(&arguments[0], Pointer(_)) => { no_types()?; (arguments.to_vec(), Void) - } + }, "__wave_async_interest" => { no_types()?; (vec![Int(64), Int(32), Int(64)], future(Int(32))) - } + }, "__wave_async_sleep" => { no_types()?; (vec![Int(64)], future(Int(32))) - } + }, "__wave_async_cancel_join" => { no_types()?; let t = scalar()?; (vec![future(t)], future(Void)) - } + }, "__wave_async_io" => { no_types()?; - ( - vec![Int(64), ptr(Uint(8)), Int(64), Int(32), Int(64)], - future(Int(64)), - ) - } + (vec![Int(64), ptr(Uint(8)), Int(64), Int(32), Int(64)], future(Int(64))) + }, "__wave_async_windows_notify_address" => { no_types()?; (vec![], ptr(Uint(8))) - } + }, "__wave_async_close_fd" => { no_types()?; (vec![Int(64)], Void) - } + }, "__wave_async_invoke" => { no_types()?; (vec![ptr(Uint(8)), ptr(Uint(8)), Int(64)], Bool) - } + }, _ => return Err(format!("invalid async intrinsic {name}")), }) } @@ -146,11 +133,9 @@ pub fn runtime_symbols(name: &str) -> &'static [&'static str] { "__wave_async_free_slot" => &["__wave_task_free"], "__wave_async_create" => &["__wave_task_new"], "__wave_async_take" => &["__wave_task_result", "__wave_task_release"], - "__wave_async_block_on" => &[ - "__wave_task_drive", - "__wave_task_result", - "__wave_task_release", - ], + "__wave_async_block_on" => { + &["__wave_task_drive", "__wave_task_result", "__wave_task_release"] + }, "__wave_async_ready" => &["__wave_task_ready"], "__wave_async_wait" => &["__wave_task_wait"], "__wave_async_complete" => &["__wave_task_complete"], diff --git a/front/parser/src/expr/binary.rs b/front/parser/src/expr/binary.rs index bf00654d..c360e436 100644 --- a/front/parser/src/expr/binary.rs +++ b/front/parser/src/expr/binary.rs @@ -72,23 +72,16 @@ where ) -> Result<(), ParseError> { let right = values.pop().expect("binary right operand"); let left = values.pop().expect("binary left operand"); - values.push(crate::expression_depth::parsed( - Expression::binary(left, op, right), - token, - )?); + values.push(crate::expression_depth::parsed(Expression::binary(left, op, right), token)?); Ok(()) } let mut values = vec![first]; let mut operators: Vec<(Operator, u8)> = Vec::new(); - while let Some((op, precedence)) = tokens - .peek() - .and_then(|token| binary_operator(&token.token_type)) + while let Some((op, precedence)) = + tokens.peek().and_then(|token| binary_operator(&token.token_type)) { let anchor = tokens.next(); - while operators - .last() - .is_some_and(|(_, previous)| *previous >= precedence) - { + while operators.last().is_some_and(|(_, previous)| *previous >= precedence) { let (previous, _) = operators.pop().unwrap(); fold(&mut values, previous, anchor)?; } @@ -126,11 +119,7 @@ where let target_type = parse_type_from_stream(tokens).ok_or_else(|| { ParseError::expected_at(tokens.peek().copied(), anchor, "type", "cast expression") })?; - expr = Expression::Cast { - expr: Box::new(expr), - target_type, - } - .with_span( + expr = Expression::Cast { expr: Box::new(expr), target_type }.with_span( first .as_ref() .zip(lexer::consumed_span(before, tokens)) diff --git a/front/parser/src/expr/helpers.rs b/front/parser/src/expr/helpers.rs index e6e25e4f..7b8eb0cb 100644 --- a/front/parser/src/expr/helpers.rs +++ b/front/parser/src/expr/helpers.rs @@ -51,11 +51,8 @@ fn parse_lvalue_tail( let dot = tokens.next(); let field = identifier(tokens, dot, "member access")?; - base = Expression::FieldAccess { - object: Box::new(base), - field, - }; - } + base = Expression::FieldAccess { object: Box::new(base), field }; + }, // a[b] Some(TokenType::Lbrack) => { @@ -64,11 +61,8 @@ fn parse_lvalue_tail( let idx = parse_expression(tokens)?; expect_token(tokens, opener, TokenType::Rbrack, "']'", "index expression")?; - base = Expression::IndexAccess { - target: Box::new(base), - index: Box::new(idx), - }; - } + base = Expression::IndexAccess { target: Box::new(base), index: Box::new(idx) }; + }, _ => break, } @@ -86,13 +80,13 @@ pub fn parse_expression_from_token( TokenType::Identifier(name) => { let base = Expression::Variable(name.clone()); parse_lvalue_tail(base, tokens) - } + }, TokenType::Deref => { let _nesting = crate::expression_depth::Nesting::enter(Some(first_token))?; let inner = parse_unary_expression(tokens)?; Ok(Expression::Deref(Box::new(inner))) - } + }, _ => Err(ParseError::expected_at( Some(first_token), diff --git a/front/parser/src/expr/postfix.rs b/front/parser/src/expr/postfix.rs index 047ab188..6972323a 100644 --- a/front/parser/src/expr/postfix.rs +++ b/front/parser/src/expr/postfix.rs @@ -40,10 +40,7 @@ where match tokens.peek().map(|t| &t.token_type) { Some(TokenType::Dot) => { let dot = tokens.next(); - focus = tokens - .peek() - .and_then(|token| token.span.clone()) - .map(Box::new); + focus = tokens.peek().and_then(|token| token.span.clone()).map(Box::new); let name = identifier(tokens, dot, "member access")?; let mut type_args = Vec::new(); if peek_is_generic_call(tokens) { @@ -59,10 +56,7 @@ where ); } } - if tokens - .peek() - .is_some_and(|token| token.token_type == TokenType::Lparen) - { + if tokens.peek().is_some_and(|token| token.token_type == TokenType::Lparen) { let args = argument_list(tokens, TokenType::Rparen, "')'", "method call")?; let base_expr = expr; expr = Expression::MethodCall { @@ -74,12 +68,9 @@ where } else { // ----- FieldAccess ----- let base_expr = expr; - expr = Expression::FieldAccess { - object: Box::new(base_expr), - field: name, - }; + expr = Expression::FieldAccess { object: Box::new(base_expr), field: name }; } - } + }, Some(TokenType::Lbrack) => { let _nesting = crate::expression_depth::Nesting::enter(tokens.peek().copied())?; @@ -92,7 +83,7 @@ where target: Box::new(base_expr), index: Box::new(index_expr), }; - } + }, Some(TokenType::Increment) => { let operator = tokens.next(); // consume '++' @@ -107,13 +98,10 @@ where } let base = expr; - expr = Expression::IncDec { - kind: IncDecKind::PostInc, - target: Box::new(base), - }; + expr = Expression::IncDec { kind: IncDecKind::PostInc, target: Box::new(base) }; return crate::expression_depth::parsed(expr, operator); - } + }, Some(TokenType::Decrement) => { let operator = tokens.next(); // consume '--' @@ -128,24 +116,20 @@ where } let base = expr; - expr = Expression::IncDec { - kind: IncDecKind::PostDec, - target: Box::new(base), - }; + expr = Expression::IncDec { kind: IncDecKind::PostDec, target: Box::new(base) }; return crate::expression_depth::parsed(expr, operator); - } + }, _ => break, } - let span = first - .as_ref() - .zip(lexer::consumed_span(before.clone(), tokens)) - .map(|(first, last)| { + let span = first.as_ref().zip(lexer::consumed_span(before.clone(), tokens)).map( + |(first, last)| { let mut span = first.through(&last); span.focus = focus; span - }); + }, + ); expr = crate::expression_depth::parsed(expr.with_span(span), tokens.peek().copied())?; } diff --git a/front/parser/src/expr/primary.rs b/front/parser/src/expr/primary.rs index 4227d683..3e4f53e2 100644 --- a/front/parser/src/expr/primary.rs +++ b/front/parser/src/expr/primary.rs @@ -97,12 +97,7 @@ where tokens.next(); Ok(()) } else { - Err(ParseError::expected_at( - tokens.peek().copied(), - anchor, - expected, - context, - )) + Err(ParseError::expected_at(tokens.peek().copied(), anchor, expected, context)) } } @@ -115,20 +110,12 @@ where T: Iterator + Clone, { match tokens.peek().copied() { - Some(Token { - token_type: TokenType::Identifier(name), - .. - }) => { + Some(Token { token_type: TokenType::Identifier(name), .. }) => { let name = name.clone(); tokens.next(); Ok(name) - } - found => Err(ParseError::expected_at( - found, - anchor, - "identifier", - context, - )), + }, + found => Err(ParseError::expected_at(found, anchor, "identifier", context)), } } @@ -144,16 +131,8 @@ where let _nesting = crate::expression_depth::Nesting::enter(tokens.peek().copied())?; let opener = tokens.next(); let mut args = Vec::new(); - if tokens - .peek() - .is_none_or(|token| token.token_type == TokenType::Eof) - { - return Err(ParseError::expected_at( - tokens.peek().copied(), - opener, - spelling, - context, - )); + if tokens.peek().is_none_or(|token| token.token_type == TokenType::Eof) { + return Err(ParseError::expected_at(tokens.peek().copied(), opener, spelling, context)); } if tokens.peek().is_some_and(|token| token.token_type == close) { tokens.next(); @@ -161,10 +140,7 @@ where } loop { args.push(parse_expression(tokens)?); - if tokens - .peek() - .is_some_and(|token| token.token_type == TokenType::Comma) - { + if tokens.peek().is_some_and(|token| token.token_type == TokenType::Comma) { tokens.next(); } else { expect_token(tokens, opener, close, spelling, context)?; @@ -182,10 +158,7 @@ where let _nesting = crate::expression_depth::Nesting::enter(tokens.peek().copied())?; let opener = tokens.next(); let mut fields = Vec::new(); - while !tokens - .peek() - .is_some_and(|token| token.token_type == TokenType::Rbrace) - { + while !tokens.peek().is_some_and(|token| token.token_type == TokenType::Rbrace) { let name = struct_field_name(tokens, opener)?; fields.push((name, parse_expression(tokens)?)); if finish_struct_field(tokens, opener)? { @@ -205,10 +178,7 @@ fn struct_field_name<'a, T>( where T: Iterator + Clone, { - if tokens - .peek() - .is_none_or(|token| token.token_type == TokenType::Eof) - { + if tokens.peek().is_none_or(|token| token.token_type == TokenType::Eof) { return Err(ParseError::expected_at( tokens.peek().copied(), opener, @@ -217,13 +187,7 @@ where )); } let name = identifier(tokens, opener, "struct literal field")?; - expect_token( - tokens, - opener, - TokenType::Colon, - "':'", - "struct literal field", - )?; + expect_token(tokens, opener, TokenType::Colon, "':'", "struct literal field")?; Ok(name) } @@ -238,7 +202,7 @@ where Some(TokenType::Comma) => { tokens.next(); Ok(false) - } + }, Some(TokenType::Rbrace) => Ok(true), _ => Err(ParseError::expected_at( tokens.peek().copied(), @@ -294,11 +258,7 @@ where let (name, type_args) = parse_expression_name(tokens, name)?; match tokens.peek().map(|token| &token.token_type) { Some(TokenType::Lparen) => argument_list(tokens, TokenType::Rparen, "')'", "function call") - .map(|args| Expression::FunctionCall { - name, - type_args, - args, - }), + .map(|args| Expression::FunctionCall { name, type_args, args }), Some(TokenType::Lbrace) => parse_struct_literal_fields(tokens) .map(|fields| Expression::StructLiteral { name, fields }), _ => Ok(Expression::Variable(name)), @@ -314,10 +274,7 @@ where { let mut name = name.to_owned(); tokens.next(); - while tokens - .peek() - .is_some_and(|token| token.token_type == TokenType::DoubleColon) - { + while tokens.peek().is_some_and(|token| token.token_type == TokenType::DoubleColon) { let separator = tokens.next(); let segment = identifier(tokens, separator, "qualified name")?; name.push_str("::"); @@ -377,12 +334,7 @@ where let _nesting = crate::expression_depth::Nesting::enter(Some(token))?; tokens.next(); let (instructions, inputs, outputs, clobbers) = parse_asm_body(tokens)?; - Ok(Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - }) + Ok(Expression::AsmBlock { instructions, inputs, outputs, clobbers }) } fn parse_literal<'a, T>(tokens: &mut Peekable, token: &Token) -> Result @@ -393,27 +345,27 @@ where TokenType::IntLiteral(s) => { tokens.next(); Ok(Expression::Literal(Literal::Int(s.clone()))) - } + }, TokenType::Float(value) => { tokens.next(); Ok(Expression::Literal(Literal::Float(*value))) - } + }, TokenType::CharLiteral(c) => { tokens.next(); Ok(Expression::Literal(Literal::Char(*c))) - } + }, TokenType::BoolLiteral(b) => { tokens.next(); Ok(Expression::Literal(Literal::Bool(*b))) - } + }, TokenType::String(s) => { tokens.next(); Ok(Expression::Literal(Literal::String(s.clone()))) - } + }, TokenType::Null => { tokens.next(); Ok(Expression::Null) - } + }, _ => Err(ParseError::expected_at( Some(token), Some(token), @@ -430,12 +382,6 @@ where let _nesting = crate::expression_depth::Nesting::enter(Some(token))?; let opener = tokens.next(); let inner = parse_expression(tokens)?; - expect_token( - tokens, - opener, - TokenType::Rparen, - "')'", - "grouped expression", - )?; + expect_token(tokens, opener, TokenType::Rparen, "')'", "grouped expression")?; Ok(Expression::Grouped(Box::new(inner))) } diff --git a/front/parser/src/expr/unary.rs b/front/parser/src/expr/unary.rs index d370c52f..dd0b1a24 100644 --- a/front/parser/src/expr/unary.rs +++ b/front/parser/src/expr/unary.rs @@ -88,13 +88,9 @@ where fn apply_prefix(token: &Token, inner: Expression) -> Result { Ok(match token.token_type { TokenType::Await => Expression::Await(Box::new(inner)), - TokenType::Not => Expression::Unary { - operator: Operator::Not, - expr: Box::new(inner), - }, - TokenType::BitwiseNot => Expression::Unary { - operator: Operator::BitwiseNot, - expr: Box::new(inner), + TokenType::Not => Expression::Unary { operator: Operator::Not, expr: Box::new(inner) }, + TokenType::BitwiseNot => { + Expression::Unary { operator: Operator::BitwiseNot, expr: Box::new(inner) } }, TokenType::AddressOf => Expression::AddressOf(Box::new(inner)), TokenType::Deref => Expression::Deref(Box::new(inner)), @@ -115,18 +111,13 @@ fn apply_prefix(token: &Token, inner: Expression) -> Result match inner.into_unspanned() { Expression::Literal(Literal::Int(s)) => Expression::Literal(Literal::Int( - s.strip_prefix('-') - .map(str::to_string) - .unwrap_or_else(|| format!("-{s}")), + s.strip_prefix('-').map(str::to_string).unwrap_or_else(|| format!("-{s}")), )), Expression::Literal(Literal::Float(f)) => Expression::Literal(Literal::Float(-f)), - other => Expression::Unary { - operator: Operator::Neg, - expr: Box::new(other), - }, + other => Expression::Unary { operator: Operator::Neg, expr: Box::new(other) }, }, TokenType::Plus => inner, _ => unreachable!("prefix operator was checked"), diff --git a/front/parser/src/expression_depth.rs b/front/parser/src/expression_depth.rs index 25a5e3c3..1ed7136b 100644 --- a/front/parser/src/expression_depth.rs +++ b/front/parser/src/expression_depth.rs @@ -52,10 +52,7 @@ fn check_expression(expr: &Expression, base: usize) -> Result<(), ParseError> { pending.push((value, depth, Some(span))); continue; } - if matches!( - expr, - Expression::Literal(_) | Expression::Variable(_) | Expression::Null - ) { + if matches!(expr, Expression::Literal(_) | Expression::Variable(_) | Expression::Null) { continue; } let depth = depth + 1; @@ -107,7 +104,7 @@ pub fn validate(nodes: &[ASTNode]) -> Result<(), SemanticDiagnostic> { help: HELP.into(), }); } - } + }, Work::Function(function) => { for param in &function.parameters { if let Some(value) = ¶m.initial_value { @@ -115,7 +112,7 @@ pub fn validate(nodes: &[ASTNode]) -> Result<(), SemanticDiagnostic> { } } pending.extend(function.body.iter().map(Work::Node)); - } + }, Work::Node(node) => match node { ASTNode::Located { value, .. } => pending.push(Work::Node(value)), ASTNode::Function(f) => pending.push(Work::Function(f)), @@ -126,28 +123,23 @@ pub fn validate(nodes: &[ASTNode]) -> Result<(), SemanticDiagnostic> { if let Some(e) = &v.initial_value { pending.push(Work::Expression(e)); } - } + }, ASTNode::Expression(e) => pending.push(Work::Expression(e)), - ASTNode::ExternFunction(_) => {} + ASTNode::ExternFunction(_) => {}, ASTNode::Program(p) => { if let Some(e) = &p.initial_value { pending.push(Work::Expression(e)); } - } - ASTNode::TypeAlias(_) | ASTNode::Enum(_) | ASTNode::Variant(_) => {} + }, + ASTNode::TypeAlias(_) | ASTNode::Enum(_) | ASTNode::Variant(_) => {}, }, Work::Statement(statement) => match statement { StatementNode::PrintFormat { args, .. } | StatementNode::PrintlnFormat { args, .. } | StatementNode::Input { args, .. } => { pending.extend(args.iter().map(Work::Expression)) - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { pending.push(Work::Expression(condition)); pending.extend(body.iter().map(Work::Node)); if let Some(blocks) = else_if_blocks { @@ -159,38 +151,28 @@ pub fn validate(nodes: &[ASTNode]) -> Result<(), SemanticDiagnostic> { if let Some(body) = else_block { pending.extend(body.iter().map(Work::Node)); } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + }, + StatementNode::For { initialization, condition, increment, body } => { pending.push(Work::Node(initialization)); pending.push(Work::Expression(condition)); pending.push(Work::Expression(increment)); pending.extend(body.iter().map(Work::Node)); - } + }, StatementNode::While { condition, body } => { pending.push(Work::Expression(condition)); pending.extend(body.iter().map(Work::Node)); - } + }, StatementNode::Match { value, arms } => { pending.push(Work::Expression(value)); for arm in arms { pending.extend(arm.body.iter().map(Work::Node)); } - } + }, StatementNode::Assign { value, .. } | StatementNode::Expression(value) | StatementNode::Return(Some(value)) => pending.push(Work::Expression(value)), - StatementNode::AsmBlock { - inputs, outputs, .. - } => pending.extend( - inputs - .iter() - .chain(outputs.iter()) - .map(|(_, e)| Work::Expression(e)), + StatementNode::AsmBlock { inputs, outputs, .. } => pending.extend( + inputs.iter().chain(outputs.iter()).map(|(_, e)| Work::Expression(e)), ), StatementNode::Print(_) | StatementNode::Println(_) @@ -198,7 +180,7 @@ pub fn validate(nodes: &[ASTNode]) -> Result<(), SemanticDiagnostic> { | StatementNode::Import(_) | StatementNode::Break | StatementNode::Continue - | StatementNode::Return(None) => {} + | StatementNode::Return(None) => {}, }, } } diff --git a/front/parser/src/generics.rs b/front/parser/src/generics.rs index e19173c3..b8c205ad 100644 --- a/front/parser/src/generics.rs +++ b/front/parser/src/generics.rs @@ -85,11 +85,7 @@ pub fn monomorphize_generics(ast: Vec) -> Result, String> } match node.unspanned() { ASTNode::Function(f) => { - if env - .function_parameters - .insert(f.name.clone(), f.parameters.clone()) - .is_some() - { + if env.function_parameters.insert(f.name.clone(), f.parameters.clone()).is_some() { return Err(format!("duplicate function '{}'", f.name)); } @@ -97,33 +93,24 @@ pub fn monomorphize_generics(ast: Vec) -> Result, String> continue; } - if env - .function_templates - .insert(f.name.clone(), f.clone()) - .is_some() - { + if env.function_templates.insert(f.name.clone(), f.clone()).is_some() { return Err(format!("duplicate generic function template '{}'", f.name)); } - } + }, #[allow(clippy::collapsible_match)] ASTNode::Struct(s) if !s.generic_params.is_empty() => { - if env - .struct_templates - .insert(s.name.clone(), s.clone()) - .is_some() - { + if env.struct_templates.insert(s.name.clone(), s.clone()).is_some() { return Err(format!("duplicate generic struct template '{}'", s.name)); } - } + }, ASTNode::Variant(variant) => { env.variant_generic_params .insert(variant.name.clone(), variant.generic_params.clone()); if !variant.generic_params.is_empty() { - env.variant_templates - .insert(variant.name.clone(), variant.clone()); + env.variant_templates.insert(variant.name.clone(), variant.clone()); } - } - _ => {} + }, + _ => {}, } } @@ -139,88 +126,51 @@ pub fn monomorphize_generics(ast: Vec) -> Result, String> ASTNode::Located { .. } => unreachable!("unspanned root"), ASTNode::Function(f) => { if f.generic_params.is_empty() { - out.push(ASTNode::Function(rewrite_function( - f, - &empty_subst, - &mut env, - )?)); + out.push(ASTNode::Function(rewrite_function(f, &empty_subst, &mut env)?)); } - } + }, ASTNode::Struct(s) if s.generic_params.is_empty() => { out.push(ASTNode::Struct(rewrite_struct(s, &empty_subst, &mut env)?)); - } + }, ASTNode::Struct(s) => out.push(ASTNode::Struct(s)), ASTNode::Variant(variant) => { if variant.generic_params.is_empty() { - out.push(ASTNode::Variant(rewrite_variant( - variant, - &empty_subst, - &mut env, - )?)); + out.push(ASTNode::Variant(rewrite_variant(variant, &empty_subst, &mut env)?)); } else { out.push(ASTNode::Variant(variant)); } - } + }, ASTNode::Variable(v) => { - out.push(ASTNode::Variable(rewrite_variable( - v, - &empty_subst, - &mut env, - )?)); - } + out.push(ASTNode::Variable(rewrite_variable(v, &empty_subst, &mut env)?)); + }, ASTNode::ExternFunction(e) => { - out.push(ASTNode::ExternFunction(rewrite_extern( - e, - &empty_subst, - &mut env, - )?)); - } + out.push(ASTNode::ExternFunction(rewrite_extern(e, &empty_subst, &mut env)?)); + }, ASTNode::ProtoImpl(p) => { - out.push(ASTNode::ProtoImpl(rewrite_proto( - p, - &empty_subst, - &mut env, - )?)); - } - ASTNode::TypeAlias(TypeAliasNode { - name, - target, - visibility, - }) => { + out.push(ASTNode::ProtoImpl(rewrite_proto(p, &empty_subst, &mut env)?)); + }, + ASTNode::TypeAlias(TypeAliasNode { name, target, visibility }) => { out.push(ASTNode::TypeAlias(TypeAliasNode { name, target: rewrite_wave_type(&target, &empty_subst, &mut env)?, visibility, })); - } - ASTNode::Enum(EnumNode { - name, - repr_type, - variants, - visibility, - }) => { + }, + ASTNode::Enum(EnumNode { name, repr_type, variants, visibility }) => { out.push(ASTNode::Enum(EnumNode { name, repr_type: rewrite_wave_type(&repr_type, &empty_subst, &mut env)?, variants, visibility, })); - } + }, ASTNode::Statement(stmt) => { - out.push(ASTNode::Statement(rewrite_statement( - stmt, - &empty_subst, - &mut env, - )?)); - } + out.push(ASTNode::Statement(rewrite_statement(stmt, &empty_subst, &mut env)?)); + }, ASTNode::Expression(expr) => { - out.push(ASTNode::Expression(rewrite_expression( - expr, - &empty_subst, - &mut env, - )?)); - } + out.push(ASTNode::Expression(rewrite_expression(expr, &empty_subst, &mut env)?)); + }, ASTNode::Program(p) => out.push(ASTNode::Program(p)), } for node in &mut out[first_output..] { @@ -285,17 +235,10 @@ fn rewrite_function( .map(|p| rewrite_parameter(p, subst, env)) .collect::, _>>()?; - f.return_type = f - .return_type - .as_ref() - .map(|t| rewrite_wave_type(t, subst, env)) - .transpose()?; + f.return_type = f.return_type.as_ref().map(|t| rewrite_wave_type(t, subst, env)).transpose()?; - f.body = f - .body - .into_iter() - .map(|n| rewrite_node(n, subst, env)) - .collect::, _>>()?; + f.body = + f.body.into_iter().map(|n| rewrite_node(n, subst, env)).collect::, _>>()?; Ok(f) } @@ -375,11 +318,8 @@ fn rewrite_variable( env: &mut GenericEnv, ) -> Result { v.type_name = rewrite_wave_type(&v.type_name, subst, env)?; - v.initial_value = v - .initial_value - .as_ref() - .map(|e| rewrite_expression(e.clone(), subst, env)) - .transpose()?; + v.initial_value = + v.initial_value.as_ref().map(|e| rewrite_expression(e.clone(), subst, env)).transpose()?; Ok(v) } @@ -391,7 +331,7 @@ fn rewrite_node( match node { ASTNode::Located { value, span } => { Ok(rewrite_node(*value, subst, env)?.with_span(Some(span))) - } + }, ASTNode::Variable(v) => Ok(ASTNode::Variable(rewrite_variable(v, subst, env)?)), ASTNode::Statement(s) => Ok(ASTNode::Statement(rewrite_statement(s, subst, env)?)), ASTNode::Expression(e) => Ok(ASTNode::Expression(rewrite_expression(e, subst, env)?)), @@ -400,26 +340,21 @@ fn rewrite_node( ASTNode::Variant(variant) => Ok(ASTNode::Variant(rewrite_variant(variant, subst, env)?)), ASTNode::ExternFunction(e) => Ok(ASTNode::ExternFunction(rewrite_extern(e, subst, env)?)), ASTNode::ProtoImpl(p) => Ok(ASTNode::ProtoImpl(rewrite_proto(p, subst, env)?)), - ASTNode::TypeAlias(TypeAliasNode { - name, - target, - visibility, - }) => Ok(ASTNode::TypeAlias(TypeAliasNode { - name, - target: rewrite_wave_type(&target, subst, env)?, - visibility, - })), - ASTNode::Enum(EnumNode { - name, - repr_type, - variants, - visibility, - }) => Ok(ASTNode::Enum(EnumNode { - name, - repr_type: rewrite_wave_type(&repr_type, subst, env)?, - variants, - visibility, - })), + ASTNode::TypeAlias(TypeAliasNode { name, target, visibility }) => { + Ok(ASTNode::TypeAlias(TypeAliasNode { + name, + target: rewrite_wave_type(&target, subst, env)?, + visibility, + })) + }, + ASTNode::Enum(EnumNode { name, repr_type, variants, visibility }) => { + Ok(ASTNode::Enum(EnumNode { + name, + repr_type: rewrite_wave_type(&repr_type, subst, env)?, + variants, + visibility, + })) + }, ASTNode::Program(p) => Ok(ASTNode::Program(p)), } } @@ -430,55 +365,46 @@ fn rewrite_statement( env: &mut GenericEnv, ) -> Result { match stmt { - StatementNode::PrintFormat { format, args } => Ok(StatementNode::PrintFormat { - format, - args: rewrite_expr_list(args, subst, env)?, - }), - StatementNode::PrintlnFormat { format, args } => Ok(StatementNode::PrintlnFormat { - format, - args: rewrite_expr_list(args, subst, env)?, - }), - StatementNode::Input { format, args } => Ok(StatementNode::Input { - format, - args: rewrite_expr_list(args, subst, env)?, - }), - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => Ok(StatementNode::If { - condition: rewrite_expression(condition, subst, env)?, - body: rewrite_node_list(body, subst, env)?, - else_if_blocks: else_if_blocks - .map(|blocks| { - blocks - .into_iter() - .map(|(cond, body)| { - Ok(( - rewrite_expression(cond, subst, env)?, - rewrite_node_list(body, subst, env)?, - )) - }) - .collect::, String>>() - }) - .transpose()? - .map(Box::new), - else_block: else_block - .map(|body| rewrite_node_list(*body, subst, env).map(Box::new)) - .transpose()?, - }), - StatementNode::For { - initialization, - condition, - increment, - body, - } => Ok(StatementNode::For { - initialization: Box::new(rewrite_node(*initialization, subst, env)?), - condition: rewrite_expression(condition, subst, env)?, - increment: rewrite_expression(increment, subst, env)?, - body: rewrite_node_list(body, subst, env)?, - }), + StatementNode::PrintFormat { format, args } => { + Ok(StatementNode::PrintFormat { format, args: rewrite_expr_list(args, subst, env)? }) + }, + StatementNode::PrintlnFormat { format, args } => { + Ok(StatementNode::PrintlnFormat { format, args: rewrite_expr_list(args, subst, env)? }) + }, + StatementNode::Input { format, args } => { + Ok(StatementNode::Input { format, args: rewrite_expr_list(args, subst, env)? }) + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { + Ok(StatementNode::If { + condition: rewrite_expression(condition, subst, env)?, + body: rewrite_node_list(body, subst, env)?, + else_if_blocks: else_if_blocks + .map(|blocks| { + blocks + .into_iter() + .map(|(cond, body)| { + Ok(( + rewrite_expression(cond, subst, env)?, + rewrite_node_list(body, subst, env)?, + )) + }) + .collect::, String>>() + }) + .transpose()? + .map(Box::new), + else_block: else_block + .map(|body| rewrite_node_list(*body, subst, env).map(Box::new)) + .transpose()?, + }) + }, + StatementNode::For { initialization, condition, increment, body } => { + Ok(StatementNode::For { + initialization: Box::new(rewrite_node(*initialization, subst, env)?), + condition: rewrite_expression(condition, subst, env)?, + increment: rewrite_expression(increment, subst, env)?, + body: rewrite_node_list(body, subst, env)?, + }) + }, StatementNode::While { condition, body } => Ok(StatementNode::While { condition: rewrite_expression(condition, subst, env)?, body: rewrite_node_list(body, subst, env)?, @@ -496,33 +422,29 @@ fn rewrite_statement( }) .collect::, String>>()?, }), - StatementNode::Assign { variable, value } => Ok(StatementNode::Assign { - variable, - value: rewrite_expression(value, subst, env)?, - }), - StatementNode::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => Ok(StatementNode::AsmBlock { - instructions, - inputs: inputs - .into_iter() - .map(|(r, e)| Ok((r, rewrite_expression(e, subst, env)?))) - .collect::, String>>()?, - outputs: outputs - .into_iter() - .map(|(r, e)| Ok((r, rewrite_expression(e, subst, env)?))) - .collect::, String>>()?, - clobbers, - }), - StatementNode::Expression(e) => Ok(StatementNode::Expression(rewrite_expression( - e, subst, env, - )?)), - StatementNode::Return(v) => Ok(StatementNode::Return( - v.map(|e| rewrite_expression(e, subst, env)).transpose()?, - )), + StatementNode::Assign { variable, value } => { + Ok(StatementNode::Assign { variable, value: rewrite_expression(value, subst, env)? }) + }, + StatementNode::AsmBlock { instructions, inputs, outputs, clobbers } => { + Ok(StatementNode::AsmBlock { + instructions, + inputs: inputs + .into_iter() + .map(|(r, e)| Ok((r, rewrite_expression(e, subst, env)?))) + .collect::, String>>()?, + outputs: outputs + .into_iter() + .map(|(r, e)| Ok((r, rewrite_expression(e, subst, env)?))) + .collect::, String>>()?, + clobbers, + }) + }, + StatementNode::Expression(e) => { + Ok(StatementNode::Expression(rewrite_expression(e, subst, env)?)) + }, + StatementNode::Return(v) => { + Ok(StatementNode::Return(v.map(|e| rewrite_expression(e, subst, env)).transpose()?)) + }, StatementNode::Print(s) => Ok(StatementNode::Print(s)), StatementNode::Println(s) => Ok(StatementNode::Println(s)), StatementNode::Variable(v) => Ok(StatementNode::Variable(v)), @@ -541,10 +463,7 @@ fn rewrite_expr_list( subst: &HashMap, env: &mut GenericEnv, ) -> Result, String> { - exprs - .into_iter() - .map(|e| rewrite_expression(e, subst, env)) - .collect() + exprs.into_iter().map(|e| rewrite_expression(e, subst, env)).collect() } fn rewrite_node_list( @@ -552,10 +471,7 @@ fn rewrite_node_list( subst: &HashMap, env: &mut GenericEnv, ) -> Result, String> { - nodes - .into_iter() - .map(|n| rewrite_node(n, subst, env)) - .collect() + nodes.into_iter().map(|n| rewrite_node(n, subst, env)).collect() } fn append_default_arguments( @@ -597,16 +513,15 @@ fn rewrite_wave_type( env: &mut GenericEnv, ) -> Result { match ty { - WaveType::Future(inner) => Ok(WaveType::Future(Box::new(rewrite_wave_type( - inner, subst, env, - )?))), - WaveType::Pointer(inner) => Ok(WaveType::Pointer(Box::new(rewrite_wave_type( - inner, subst, env, - )?))), - WaveType::Array(inner, n) => Ok(WaveType::Array( - Box::new(rewrite_wave_type(inner, subst, env)?), - *n, - )), + WaveType::Future(inner) => { + Ok(WaveType::Future(Box::new(rewrite_wave_type(inner, subst, env)?))) + }, + WaveType::Pointer(inner) => { + Ok(WaveType::Pointer(Box::new(rewrite_wave_type(inner, subst, env)?))) + }, + WaveType::Array(inner, n) => { + Ok(WaveType::Array(Box::new(rewrite_wave_type(inner, subst, env)?), *n)) + }, WaveType::Struct(name) => rewrite_struct_type(name, subst, env), WaveType::Variant(name) => Ok(WaveType::Variant(name.clone())), _ => Ok(ty.clone()), @@ -643,10 +558,7 @@ fn rewrite_struct_type( return Ok(WaveType::Struct(instantiated)); } if !env.struct_templates.contains_key(&base) { - return Err(format!( - "generic type '{}' is not declared as a generic struct", - base - )); + return Err(format!("generic type '{}' is not declared as a generic struct", base)); } let mut concrete_args: Vec = Vec::with_capacity(arg_strs.len()); @@ -661,10 +573,7 @@ fn rewrite_struct_type( if let Some(template) = env.struct_templates.get(name) { if !template.generic_params.is_empty() { - return Err(format!( - "generic struct '{}' requires explicit type arguments", - name - )); + return Err(format!("generic struct '{}' requires explicit type arguments", name)); } } @@ -692,16 +601,11 @@ fn ensure_variant_instance( let instance_name = format!( "{}<{}>", base, - args.iter() - .map(display_type_for_application) - .collect::>() - .join(",") + args.iter().map(display_type_for_application).collect::>().join(",") ); if let Some(span) = env.origin_spans.get(base).cloned() { - env.origin_spans.insert( - instance_name.clone(), - span.generated(format!("specialization of {base}")), - ); + env.origin_spans + .insert(instance_name.clone(), span.generated(format!("specialization of {base}"))); } if env.variant_instances.contains_key(&instance_name) || env.variant_in_progress.contains(&instance_name) @@ -721,8 +625,7 @@ fn ensure_variant_instance( instance.generic_params.clear(); instance = rewrite_variant(instance, &substitutions, env)?; env.variant_in_progress.remove(&instance_name); - env.variant_instances - .insert(instance_name.clone(), instance); + env.variant_instances.insert(instance_name.clone(), instance); Ok(instance_name) } @@ -762,10 +665,8 @@ fn ensure_struct_instance( let inst_name = mangle_instance_name(base, args); if let Some(span) = env.origin_spans.get(base).cloned() { - env.origin_spans.insert( - inst_name.clone(), - span.generated(format!("specialization of {base}")), - ); + env.origin_spans + .insert(inst_name.clone(), span.generated(format!("specialization of {base}"))); } if env.struct_instances.contains_key(&inst_name) { return Ok(inst_name); @@ -784,7 +685,7 @@ fn ensure_struct_instance( if env.struct_in_progress.len() + env.variant_in_progress.len() >= 16 { return Err( - "generic instantiation depth exceeded (possible expanding recursion)".to_string(), + "generic instantiation depth exceeded (possible expanding recursion)".to_string() ); } env.struct_in_progress.insert(inst_name.clone()); @@ -795,10 +696,7 @@ fn ensure_struct_instance( instantiated = rewrite_struct(instantiated, &map, env)?; env.struct_in_progress.remove(&inst_name); - env.struct_instances - .insert(inst_name.clone(), instantiated) - .map(|_| ()) - .unwrap_or(()); + env.struct_instances.insert(inst_name.clone(), instantiated).map(|_| ()).unwrap_or(()); Ok(inst_name) } @@ -825,10 +723,8 @@ fn ensure_function_instance( let inst_name = mangle_instance_name(base, args); if let Some(span) = env.origin_spans.get(base).cloned() { - env.origin_spans.insert( - inst_name.clone(), - span.generated(format!("specialization of {base}")), - ); + env.origin_spans + .insert(inst_name.clone(), span.generated(format!("specialization of {base}"))); } if env.function_instances.contains_key(&inst_name) { return Ok(inst_name); @@ -845,7 +741,7 @@ fn ensure_function_instance( if env.function_depth >= 128 { return Err( - "generic instantiation depth exceeded (possible expanding recursion)".to_string(), + "generic instantiation depth exceeded (possible expanding recursion)".to_string() ); } env.function_in_progress.insert(inst_name.clone()); @@ -868,10 +764,7 @@ fn parse_type_application(name: &str) -> Result)>, S return Ok(None); }; if !s.ends_with('>') { - return Err(format!( - "malformed generic type '{}': missing closing '>'", - s - )); + return Err(format!("malformed generic type '{}': missing closing '>'", s)); } let base = s[..lt].trim(); @@ -880,10 +773,7 @@ fn parse_type_application(name: &str) -> Result)>, S } let inner = &s[lt + 1..s.len() - 1]; let args = split_top_level_generic_args(inner).ok_or_else(|| { - format!( - "malformed generic type '{}': invalid generic argument list", - name - ) + format!("malformed generic type '{}': invalid generic argument list", name) })?; Ok(Some((base.to_string(), args))) } @@ -944,7 +834,7 @@ fn display_type_for_application(ty: &WaveType) -> String { WaveType::Pointer(inner) => format!("ptr<{}>", display_type_for_application(inner)), WaveType::Array(inner, size) => { format!("array<{},{}>", display_type_for_application(inner), size) - } + }, WaveType::Void => "void".to_string(), WaveType::Never => "!".to_string(), WaveType::Struct(name) | WaveType::Variant(name) => name.clone(), diff --git a/front/parser/src/generics/expression.rs b/front/parser/src/generics/expression.rs index bfe69146..e386238d 100644 --- a/front/parser/src/generics/expression.rs +++ b/front/parser/src/generics/expression.rs @@ -29,6 +29,7 @@ pub(super) fn rewrite_expression( other => Ok(other), } } + fn rewrite_located( expression: Expression, subst: &HashMap, @@ -38,11 +39,7 @@ fn rewrite_located( unreachable!("expression dispatch") }; - let span = if subst.is_empty() { - span - } else { - span.generated("generic specialization") - }; + let span = if subst.is_empty() { span } else { span.generated("generic specialization") }; Ok(rewrite_expression(*value, subst, env)?.with_span(Some(span))) } @@ -51,12 +48,7 @@ fn rewrite_function_call( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::FunctionCall { - name, - type_args, - args, - } = expression - else { + let Expression::FunctionCall { name, type_args, args } = expression else { unreachable!("expression dispatch") }; @@ -65,16 +57,9 @@ fn rewrite_function_call( if type_args.is_empty() { if env.function_templates.contains_key(&name) { - return Err(format!( - "generic function '{}' requires explicit type arguments", - name - )); + return Err(format!("generic function '{}' requires explicit type arguments", name)); } - return Ok(Expression::FunctionCall { - name, - type_args, - args, - }); + return Ok(Expression::FunctionCall { name, type_args, args }); } let concrete_args: Vec = type_args @@ -84,25 +69,14 @@ fn rewrite_function_call( if crate::async_intrinsics::is_intrinsic(&name) || crate::layout_intrinsics::is_intrinsic(&name) { - return Ok(Expression::FunctionCall { - name, - type_args: concrete_args, - args, - }); + return Ok(Expression::FunctionCall { name, type_args: concrete_args, args }); } if !env.function_templates.contains_key(&name) { - return Err(format!( - "type arguments provided for non-generic function '{}'", - name - )); + return Err(format!("type arguments provided for non-generic function '{}'", name)); } let instantiated = ensure_function_instance(&name, &concrete_args, env)?; - Ok(Expression::FunctionCall { - name: instantiated, - type_args: Vec::new(), - args, - }) + Ok(Expression::FunctionCall { name: instantiated, type_args: Vec::new(), args }) } fn rewrite_method_call( @@ -110,13 +84,7 @@ fn rewrite_method_call( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::MethodCall { - object, - name, - args, - type_args, - } = expression - else { + let Expression::MethodCall { object, name, args, type_args } = expression else { unreachable!("expression dispatch") }; Ok(Expression::MethodCall { @@ -144,10 +112,7 @@ fn rewrite_struct_literal( for (fname, value) in fields { rewritten_fields.push((fname, rewrite_expression(value, subst, env)?)); } - Ok(Expression::StructLiteral { - name: rewritten_name, - fields: rewritten_fields, - }) + Ok(Expression::StructLiteral { name: rewritten_name, fields: rewritten_fields }) } fn rewrite_deref( @@ -155,12 +120,8 @@ fn rewrite_deref( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::Deref(inner) = expression else { - unreachable!("expression dispatch") - }; - Ok(Expression::Deref(Box::new(rewrite_expression( - *inner, subst, env, - )?))) + let Expression::Deref(inner) = expression else { unreachable!("expression dispatch") }; + Ok(Expression::Deref(Box::new(rewrite_expression(*inner, subst, env)?))) } fn rewrite_address_of( @@ -168,12 +129,8 @@ fn rewrite_address_of( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::AddressOf(inner) = expression else { - unreachable!("expression dispatch") - }; - Ok(Expression::AddressOf(Box::new(rewrite_expression( - *inner, subst, env, - )?))) + let Expression::AddressOf(inner) = expression else { unreachable!("expression dispatch") }; + Ok(Expression::AddressOf(Box::new(rewrite_expression(*inner, subst, env)?))) } fn rewrite_binary_expression( @@ -181,12 +138,7 @@ fn rewrite_binary_expression( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::BinaryExpression { - left, - operator, - right, - } = expression - else { + let Expression::BinaryExpression { left, operator, right } = expression else { unreachable!("expression dispatch") }; Ok(Expression::BinaryExpression { @@ -215,12 +167,8 @@ fn rewrite_array_literal( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::ArrayLiteral(items) = expression else { - unreachable!("expression dispatch") - }; - Ok(Expression::ArrayLiteral(rewrite_expr_list( - items, subst, env, - )?)) + let Expression::ArrayLiteral(items) = expression else { unreachable!("expression dispatch") }; + Ok(Expression::ArrayLiteral(rewrite_expr_list(items, subst, env)?)) } fn rewrite_await( @@ -228,12 +176,8 @@ fn rewrite_await( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::Await(inner) = expression else { - unreachable!("expression dispatch") - }; - Ok(Expression::Await(Box::new(rewrite_expression( - *inner, subst, env, - )?))) + let Expression::Await(inner) = expression else { unreachable!("expression dispatch") }; + Ok(Expression::Await(Box::new(rewrite_expression(*inner, subst, env)?))) } fn rewrite_grouped( @@ -241,12 +185,8 @@ fn rewrite_grouped( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::Grouped(inner) = expression else { - unreachable!("expression dispatch") - }; - Ok(Expression::Grouped(Box::new(rewrite_expression( - *inner, subst, env, - )?))) + let Expression::Grouped(inner) = expression else { unreachable!("expression dispatch") }; + Ok(Expression::Grouped(Box::new(rewrite_expression(*inner, subst, env)?))) } fn rewrite_assign_operation( @@ -254,12 +194,7 @@ fn rewrite_assign_operation( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::AssignOperation { - target, - operator, - value, - } = expression - else { + let Expression::AssignOperation { target, operator, value } = expression else { unreachable!("expression dispatch") }; Ok(Expression::AssignOperation { @@ -288,13 +223,7 @@ fn rewrite_asm_block( subst: &HashMap, env: &mut GenericEnv, ) -> Result { - let Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } = expression - else { + let Expression::AsmBlock { instructions, inputs, outputs, clobbers } = expression else { unreachable!("expression dispatch") }; Ok(Expression::AsmBlock { @@ -333,10 +262,7 @@ fn rewrite_unary( let Expression::Unary { operator, expr } = expression else { unreachable!("expression dispatch") }; - Ok(Expression::Unary { - operator, - expr: Box::new(rewrite_expression(*expr, subst, env)?), - }) + Ok(Expression::Unary { operator, expr: Box::new(rewrite_expression(*expr, subst, env)?) }) } fn rewrite_cast( @@ -361,8 +287,5 @@ fn rewrite_inc_dec( let Expression::IncDec { kind, target } = expression else { unreachable!("expression dispatch") }; - Ok(Expression::IncDec { - kind, - target: Box::new(rewrite_expression(*target, subst, env)?), - }) + Ok(Expression::IncDec { kind, target: Box::new(rewrite_expression(*target, subst, env)?) }) } diff --git a/front/parser/src/import.rs b/front/parser/src/import.rs index 8f47e939..736a362a 100644 --- a/front/parser/src/import.rs +++ b/front/parser/src/import.rs @@ -49,14 +49,8 @@ struct TargetAttrCondition<'a> { impl TargetConditionContext { fn actual_value(&self, key: &str) -> String { match key { - "arch" => self - .arch - .clone() - .unwrap_or_else(|| std::env::consts::ARCH.to_string()), - "os" => self - .os - .clone() - .unwrap_or_else(|| std::env::consts::OS.to_string()), + "arch" => self.arch.clone().unwrap_or_else(|| std::env::consts::ARCH.to_string()), + "os" => self.os.clone().unwrap_or_else(|| std::env::consts::OS.to_string()), "env" => self.env.clone().unwrap_or_default(), "abi" => self.abi.clone().unwrap_or_default(), _ => String::new(), @@ -66,12 +60,9 @@ impl TargetConditionContext { impl<'a> TargetAttrCondition<'a> { fn matches(&self, target: &TargetConditionContext) -> bool { - for (key, expected) in [ - ("arch", self.arch), - ("os", self.os), - ("env", self.env), - ("abi", self.abi), - ] { + for (key, expected) in + [("arch", self.arch), ("os", self.os), ("env", self.env), ("abi", self.abi)] + { if let Some(expected) = expected { let actual = target.actual_value(key); if normalize_target_value(key, &actual) != normalize_target_value(key, expected) { @@ -263,11 +254,7 @@ fn consume_target_item( let mut saw_semicolon = false; scan_target_item_line( - if idx == first { - &line[first_offset..] - } else { - line - }, + if idx == first { &line[first_offset..] } else { line }, in_block_comment, &mut depth, &mut seen_open, @@ -328,10 +315,7 @@ pub fn preprocess_target_attrs(source: &str, target: &TargetConditionContext) -> while idx < lines.len() { let line = lines[idx]; - if let Some(target_attr) = (comment_depth == 0) - .then(|| parse_target_attr(line)) - .flatten() - { + if let Some(target_attr) = (comment_depth == 0).then(|| parse_target_attr(line)).flatten() { // Attribute line is removed for parser compatibility, // but we keep its line slot to preserve diagnostics. out.push(" ".repeat(line.len())); @@ -387,9 +371,7 @@ pub fn preprocess_target_attrs(source: &str, target: &TargetConditionContext) -> for (i, line) in out.iter().enumerate() { result.push_str(line); let (start, original) = source_lines[i]; - let end = source_lines - .get(i + 1) - .map_or(source.len(), |(start, _)| *start); + let end = source_lines.get(i + 1).map_or(source.len(), |(start, _)| *start); result.push_str(&source[start + original.len()..end]); } result @@ -545,10 +527,7 @@ pub fn local_import_unit_with_config( if !canonical_target.starts_with(&canonical_base) { return Err(WaveError::new( WaveErrorKind::SyntaxError("Local import escapes module directory".to_string()), - format!( - "local import '{}' resolves outside its module directory", - path - ), + format!("local import '{}' resolves outside its module directory", path), path, 0, 0, @@ -556,12 +535,7 @@ pub fn local_import_unit_with_config( .with_help("remove symlink or path traversal components from the local import")); } - parse_wave_file( - &canonical_target, - &target_file_name, - already_imported, - config, - ) + parse_wave_file(&canonical_target, &target_file_name, already_imported, config) } pub fn local_import( @@ -569,10 +543,8 @@ pub fn local_import( already_imported: &mut HashSet, base_dir: &Path, ) -> Result, WaveError> { - Ok( - local_import_unit_with_config(path, already_imported, base_dir, &ImportConfig::default())? - .ast, - ) + Ok(local_import_unit_with_config(path, already_imported, base_dir, &ImportConfig::default())? + .ast) } pub fn local_import_with_config( @@ -618,13 +590,8 @@ fn external_import_unit( let valid_segment = |segment: &str| { !segment.is_empty() - && segment - .chars() - .next() - .is_some_and(|ch| ch.is_ascii_alphabetic() || ch == '_') - && segment - .chars() - .all(|ch| ch.is_ascii_alphanumeric() || ch == '_') + && segment.chars().next().is_some_and(|ch| ch.is_ascii_alphabetic() || ch == '_') + && segment.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '_') }; if !valid_segment(package) || module_parts.iter().any(|segment| !valid_segment(segment)) { @@ -646,10 +613,7 @@ fn external_import_unit( Ok(None) => { let mut err = WaveError::new( WaveErrorKind::SyntaxError("External dependency not found".to_string()), - format!( - "could not resolve external package '{}' for import '{}'", - package, path - ), + format!("could not resolve external package '{}' for import '{}'", package, path), path, 0, 0, @@ -671,13 +635,10 @@ fn external_import_unit( err = err.with_note(format!("currently configured dependency roots: {}", roots)); } return Err(err); - } + }, Err(candidates) => { - let roots = candidates - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", "); + let roots = + candidates.iter().map(|p| p.display().to_string()).collect::>().join(", "); return Err(WaveError::new( WaveErrorKind::SyntaxError("Ambiguous external package root".to_string()), @@ -691,7 +652,7 @@ fn external_import_unit( ) .with_note(format!("candidates: {}", roots)) .with_help("pin the package path explicitly with `--dep =`")); - } + }, }; if !package_root.is_dir() { @@ -710,25 +671,16 @@ fn external_import_unit( } let candidates = if module_parts.is_empty() { - vec![ - package_root.join("src/lib.wave"), - package_root.join("lib.wave"), - ] + vec![package_root.join("src/lib.wave"), package_root.join("lib.wave")] } else { let module_file = format!("{}.wave", module_parts.join("/")); - vec![ - package_root.join("src").join(&module_file), - package_root.join(&module_file), - ] + vec![package_root.join("src").join(&module_file), package_root.join(&module_file)] }; let canonical_package_root = package_root.canonicalize().map_err(|error| { WaveError::new( WaveErrorKind::SyntaxError("Canonicalization failed".to_string()), - format!( - "failed to canonicalize dependency package '{}': {}", - package, error - ), + format!("failed to canonicalize dependency package '{}': {}", package, error), path, 0, 0, @@ -751,10 +703,7 @@ fn external_import_unit( WaveErrorKind::SyntaxError( "Package import escapes dependency root".to_string(), ), - format!( - "package import '{}' resolves outside package '{}'", - path, package - ), + format!("package import '{}' resolves outside package '{}'", path, package), path, 0, 0, @@ -765,18 +714,12 @@ fn external_import_unit( } } - let searched = candidates - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", "); + let searched = + candidates.iter().map(|p| p.display().to_string()).collect::>().join(", "); Err(WaveError::new( WaveErrorKind::SyntaxError("File not found".to_string()), - format!( - "could not find external import target '{}' in package '{}'", - path, package - ), + format!("could not find external import target '{}' in package '{}'", path, package), path, 0, 0, @@ -809,37 +752,22 @@ fn std_import_unit( fn resolve_std_import_path(std_root: &Path, path: &str) -> Result { let invalid = |message: String| { - WaveError::new( - WaveErrorKind::SyntaxError("Invalid std import".into()), - message, - path, - 0, - 0, - ) + WaveError::new(WaveErrorKind::SyntaxError("Invalid std import".into()), message, path, 0, 0) }; - let rel = path - .strip_prefix("std::") - .ok_or_else(|| invalid("expected std:: module path".into()))?; + let rel = + path.strip_prefix("std::").ok_or_else(|| invalid("expected std:: module path".into()))?; if rel.split("::").any(|segment| { let mut chars = segment.chars(); - !chars - .next() - .is_some_and(|ch| ch == '_' || ch.is_alphabetic()) + !chars.next().is_some_and(|ch| ch == '_' || ch.is_alphabetic()) || !chars.all(|ch| ch == '_' || ch.is_alphanumeric()) }) { - return Err(invalid( - "std import paths must contain module names separated by `::`".into(), - )); + return Err(invalid("std import paths must contain module names separated by `::`".into())); } let root = std::fs::canonicalize(std_root) .map_err(|e| invalid(format!("cannot resolve std root: {e}")))?; let target = root.join(format!("{}.wave", rel.replace("::", "/"))); - let target = std::fs::canonicalize(&target).map_err(|e| { - invalid(format!( - "cannot resolve std import '{}': {e}", - target.display() - )) - })?; + let target = std::fs::canonicalize(&target) + .map_err(|e| invalid(format!("cannot resolve std import '{}': {e}", target.display())))?; if !target.starts_with(&root) || !target.is_file() { return Err(invalid( "std import target must be a file inside the standard-library root".into(), @@ -858,17 +786,11 @@ pub fn std_compatibility_revision(std_root: &Path) -> Result { .map_err(|error| format!("invalid '{}': {}", manifest_path.display(), error))?; if manifest.get_str("name") != Some("std") { - return Err(format!( - "invalid '{}': name must be 'std'", - manifest_path.display() - )); + return Err(format!("invalid '{}': name must be 'std'", manifest_path.display())); } let raw = manifest.get_u64("compatibility_revision").ok_or_else(|| { - format!( - "invalid '{}': compatibility_revision must be an integer", - manifest_path.display() - ) + format!("invalid '{}': compatibility_revision must be an integer", manifest_path.display()) })?; Ok(raw) } @@ -902,10 +824,7 @@ fn std_root_dir(import_path: &str) -> Result { utils::paths::std_root_dir().ok_or_else(|| { WaveError::new( WaveErrorKind::SyntaxError("std not installed".to_string()), - format!( - "{}; cannot locate std", - utils::paths::missing_home_message() - ), + format!("{}; cannot locate std", utils::paths::missing_home_message()), import_path, 0, 0, @@ -943,11 +862,7 @@ fn parse_wave_file( .to_string(); if already_imported.contains(&abs_path_str) { - return Ok(ImportedUnit { - abs_path, - ast: vec![], - source: String::new(), - }); + return Ok(ImportedUnit { abs_path, ast: vec![], source: String::new() }); } already_imported.insert(abs_path_str); @@ -967,26 +882,17 @@ fn parse_wave_file( let ast = parse_syntax_with_spans(&tokens).map_err(|e| { let (kind, phase, code) = match &e { - ParseError::Syntax(_) => ( - WaveErrorKind::SyntaxError(e.message().to_string()), - "syntax", - "E2001", - ), - ParseError::Semantic(_) => ( - WaveErrorKind::InvalidStatement(e.message().to_string()), - "semantic", - "E3001", - ), + ParseError::Syntax(_) => { + (WaveErrorKind::SyntaxError(e.message().to_string()), "syntax", "E2001") + }, + ParseError::Semantic(_) => { + (WaveErrorKind::InvalidStatement(e.message().to_string()), "semantic", "E3001") + }, }; let mut we = WaveError::new( kind, - format!( - "{} validation failed for '{}': {}", - phase, - abs_path.display(), - e.message() - ), + format!("{} validation failed for '{}': {}", phase, abs_path.display(), e.message()), display_name, e.line().max(1), e.column().max(1), @@ -994,9 +900,7 @@ fn parse_wave_file( .with_code(code) .with_source_code(content.clone()); - we = we - .with_span(e.span()) - .with_related(e.related().iter().cloned()); + we = we.with_span(e.span()).with_related(e.related().iter().cloned()); if let Some(ctx) = e.context() { we = we.with_context(ctx.to_string()); } @@ -1016,11 +920,7 @@ fn parse_wave_file( we })?; - Ok(ImportedUnit { - abs_path, - ast, - source: content, - }) + Ok(ImportedUnit { abs_path, ast, source: content }) } #[cfg(test)] @@ -1055,10 +955,7 @@ mod tests { std::fs::write(root.join("second.wave"), "pub const SECOND: i32 = 2;").unwrap(); let resolved = ResolvedStdRoot::resolve(Some(&root)).unwrap(); std::fs::write(root.join("manifest.json"), "invalid").unwrap(); - let config = ImportConfig { - std_root: Ok(resolved), - ..ImportConfig::default() - }; + let config = ImportConfig { std_root: Ok(resolved), ..ImportConfig::default() }; let mut imported = HashSet::new(); std_import_unit("std::first", &mut imported, &config).unwrap(); std_import_unit("std::second", &mut imported, &config).unwrap(); @@ -1120,17 +1017,12 @@ mod tests { lines.push("pub fun after() {}"); let source = lines.join(newline); for os in ["linux", "windows"] { - let target = TargetConditionContext { - os: Some(os.into()), - ..Default::default() - }; + let target = + TargetConditionContext { os: Some(os.into()), ..Default::default() }; let processed = preprocess_target_attrs(&source, &target); assert_eq!(processed.len(), source.len()); assert_eq!(error::span::source_lines(&processed).len(), lines.len()); - assert_eq!( - processed.find("pub fun after"), - source.find("pub fun after") - ); + assert_eq!(processed.find("pub fun after"), source.find("pub fun after")); assert_eq!( processed.contains("pub fun selected"), os == "linux", @@ -1145,10 +1037,7 @@ mod tests { #[test] fn target_directives_inside_ordinary_comments_are_inert() { - let target = TargetConditionContext { - os: Some("linux".into()), - ..Default::default() - }; + let target = TargetConditionContext { os: Some("linux".into()), ..Default::default() }; for newline in ["\n", "\r\n", "\r"] { let source = [ "/* outer", @@ -1216,10 +1105,7 @@ mod tests { ) .unwrap(); - assert_eq!( - std_compatibility_revision(&root), - Ok(STD_COMPATIBILITY_REVISION) - ); + assert_eq!(std_compatibility_revision(&root), Ok(STD_COMPATIBILITY_REVISION)); let _ = std::fs::remove_dir_all(root); } @@ -1228,11 +1114,7 @@ mod tests { let root = temp_std_root("incompatible"); let imported = root.join("fs/file.wave"); std::fs::create_dir_all(imported.parent().unwrap()).unwrap(); - std::fs::write( - &imported, - "pub fun exists(path: str) -> bool { return false; }", - ) - .unwrap(); + std::fs::write(&imported, "pub fun exists(path: str) -> bool { return false; }").unwrap(); std::fs::write( root.join("manifest.json"), "{\"name\":\"std\",\"compatibility_revision\":0}", @@ -1243,9 +1125,7 @@ mod tests { assert_eq!(error.file, imported.display().to_string()); assert!(error.message.contains("revision 0"), "{}", error.message); assert!( - error - .message - .contains(&format!("requires {}", STD_COMPATIBILITY_REVISION)), + error.message.contains(&format!("requires {}", STD_COMPATIBILITY_REVISION)), "{}", error.message ); diff --git a/front/parser/src/methods.rs b/front/parser/src/methods.rs index 4e63495c..15e90bb9 100644 --- a/front/parser/src/methods.rs +++ b/front/parser/src/methods.rs @@ -39,10 +39,7 @@ pub(crate) fn lower_generic_methods(mut ast: Vec) -> Result) -> Result args, _ => unreachable!("semantic generic call resolution"), }; @@ -67,10 +64,7 @@ pub(crate) fn lower_generic_methods(mut ast: Vec) -> Result) { *node = node.clone().with_span(span.clone()); } return; - } + }, ASTNode::Struct(s) => (&s.name, s.generic_params.clone(), &mut s.methods), ASTNode::ProtoImpl(p) => (&p.target, Vec::new(), &mut p.methods), _ => return, @@ -125,7 +119,7 @@ fn walk_node(node: &mut ASTNode, visit: &mut impl FnMut(&mut Expression)) { } } walk_nodes(&mut function.body, visit); - } + }, ASTNode::Struct(structure) => { for method in &mut structure.methods { for parameter in &mut method.parameters { @@ -135,7 +129,7 @@ fn walk_node(node: &mut ASTNode, visit: &mut impl FnMut(&mut Expression)) { } walk_nodes(&mut method.body, visit); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &mut implementation.methods { for parameter in &mut method.parameters { @@ -145,19 +139,19 @@ fn walk_node(node: &mut ASTNode, visit: &mut impl FnMut(&mut Expression)) { } walk_nodes(&mut method.body, visit); } - } + }, ASTNode::Statement(statement) => walk_statement(statement, visit), ASTNode::Variable(variable) => { if let Some(initializer) = &mut variable.initial_value { walk_expression(initializer, visit); } - } + }, ASTNode::Expression(expression) => walk_expression(expression, visit), ASTNode::ExternFunction(_) | ASTNode::Program(_) | ASTNode::TypeAlias(_) | ASTNode::Enum(_) - | ASTNode::Variant(_) => {} + | ASTNode::Variant(_) => {}, } } @@ -169,13 +163,8 @@ fn walk_statement(statement: &mut StatementNode, visit: &mut impl FnMut(&mut Exp for argument in args { walk_expression(argument, visit); } - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { walk_expression(condition, visit); walk_nodes(body, visit); if let Some(blocks) = else_if_blocks { @@ -187,46 +176,39 @@ fn walk_statement(statement: &mut StatementNode, visit: &mut impl FnMut(&mut Exp if let Some(body) = else_block { walk_nodes(body, visit); } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + }, + StatementNode::For { initialization, condition, increment, body } => { walk_node(initialization, visit); walk_expression(condition, visit); walk_expression(increment, visit); walk_nodes(body, visit); - } + }, StatementNode::While { condition, body } => { walk_expression(condition, visit); walk_nodes(body, visit); - } + }, StatementNode::Match { value, arms } => { walk_expression(value, visit); for arm in arms { walk_nodes(&mut arm.body, visit); } - } + }, StatementNode::Assign { value, .. } => walk_expression(value, visit), - StatementNode::AsmBlock { - inputs, outputs, .. - } => { + StatementNode::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter_mut().chain(outputs.iter_mut()) { walk_expression(expression, visit); } - } + }, StatementNode::Return(Some(expression)) | StatementNode::Expression(expression) => { walk_expression(expression, visit) - } + }, StatementNode::Print(_) | StatementNode::Println(_) | StatementNode::Variable(_) | StatementNode::Import(_) | StatementNode::Break | StatementNode::Continue - | StatementNode::Return(None) => {} + | StatementNode::Return(None) => {}, } } @@ -241,18 +223,18 @@ fn walk_expression(expression: &mut Expression, visit: &mut impl FnMut(&mut Expr for (_, value) in fields { walk_expression(value, visit); } - } + }, Expression::FunctionCall { args, .. } => { for argument in args { walk_expression(argument, visit); } - } + }, Expression::MethodCall { object, args, .. } => { walk_expression(object, visit); for argument in args { walk_expression(argument, visit); } - } + }, Expression::Deref(inner) | Expression::AddressOf(inner) | Expression::Await(inner) @@ -262,35 +244,23 @@ fn walk_expression(expression: &mut Expression, visit: &mut impl FnMut(&mut Expr | Expression::FieldAccess { object: inner, .. } | Expression::IncDec { target: inner, .. } => walk_expression(inner, visit), Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } - | Expression::AssignOperation { - target: left, - value: right, - .. - } - | Expression::Assignment { - target: left, - value: right, - } => { + | Expression::IndexAccess { target: left, index: right } + | Expression::AssignOperation { target: left, value: right, .. } + | Expression::Assignment { target: left, value: right } => { walk_expression(left, visit); walk_expression(right, visit); - } + }, Expression::ArrayLiteral(values) => { for value in values { walk_expression(value, visit); } - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter_mut().chain(outputs.iter_mut()) { walk_expression(expression, visit); } - } - Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {} + }, + Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {}, } visit(expression); } diff --git a/front/parser/src/parser/asm.rs b/front/parser/src/parser/asm.rs index 35e79901..71e76afd 100644 --- a/front/parser/src/parser/asm.rs +++ b/front/parser/src/parser/asm.rs @@ -24,21 +24,11 @@ use lexer::Token; use std::iter::Peekable; use std::slice::Iter; -type AsmBody = ( - Vec, - Vec<(String, Expression)>, - Vec<(String, Expression)>, - Vec, -); +type AsmBody = (Vec, Vec<(String, Expression)>, Vec<(String, Expression)>, Vec); pub fn parse_asm_block(tokens: &mut Peekable>) -> Result { let (instructions, inputs, outputs, clobbers) = parse_asm_body(tokens)?; - Ok(ASTNode::Statement(StatementNode::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - })) + Ok(ASTNode::Statement(StatementNode::AsmBlock { instructions, inputs, outputs, clobbers })) } pub(crate) fn parse_asm_body<'a, T>(tokens: &mut Peekable) -> Result @@ -54,10 +44,10 @@ where Some(TokenType::Rbrace) => { tokens.next(); break; - } + }, Some(TokenType::SemiColon | TokenType::Comma) => { tokens.next(); - } + }, Some(TokenType::String(s)) => { instructions.push(String::from_utf8(s.clone()).map_err(|_| { ParseError::expected_at( @@ -68,19 +58,19 @@ where ) })?); tokens.next(); - } + }, Some(TokenType::In) => { tokens.next(); parse_asm_inout_clause(tokens, true, &mut inputs, &mut outputs)?; - } + }, Some(TokenType::Out) => { tokens.next(); parse_asm_inout_clause(tokens, false, &mut inputs, &mut outputs)?; - } + }, Some(TokenType::Clobber) => { tokens.next(); parse_asm_clobber_clause(tokens, &mut clobbers)?; - } + }, Some(TokenType::Identifier(s)) if s == "in" || s == "out" || s == "clobber" => { let clause = s.clone(); tokens.next(); @@ -89,7 +79,7 @@ where } else { parse_asm_inout_clause(tokens, clause == "in", &mut inputs, &mut outputs)?; } - } + }, found => { return Err(ParseError::expected_at( tokens.peek().copied(), @@ -101,7 +91,7 @@ where }, "asm block", )) - } + }, } } Ok((instructions, inputs, outputs, clobbers)) @@ -112,18 +102,12 @@ where T: Iterator + Clone, { match tokens.peek().copied() { - Some(Token { - token_type: TokenType::Identifier(s), - .. - }) => { + Some(Token { token_type: TokenType::Identifier(s), .. }) => { let name = s.clone(); tokens.next(); Ok(name) - } - Some(Token { - token_type: TokenType::String(s), - .. - }) => { + }, + Some(Token { token_type: TokenType::String(s), .. }) => { let name = String::from_utf8(s.clone()).map_err(|_| { ParseError::expected_at( tokens.peek().copied(), @@ -134,13 +118,10 @@ where })?; tokens.next(); Ok(name) - } - found => Err(ParseError::expected_at( - found, - found, - "register string or identifier", - context, - )), + }, + found => { + Err(ParseError::expected_at(found, found, "register string or identifier", context)) + }, } } @@ -154,19 +135,13 @@ where let anchor = tokens.peek().copied(); let context = "asm clobber clause"; crate::expr::expect_token(tokens, anchor, TokenType::Lparen, "'('", context)?; - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Rparen) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::Rparen) { tokens.next(); return Ok(()); } loop { clobbers.push(register(tokens, context)?); - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Comma) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::Comma) { tokens.next(); } else { crate::expr::expect_token(tokens, anchor, TokenType::Rparen, "',' or ')'", context)?; @@ -185,11 +160,7 @@ where T: Iterator + Clone, { let anchor = tokens.peek().copied(); - let context = if is_input { - "asm input clause" - } else { - "asm output clause" - }; + let context = if is_input { "asm input clause" } else { "asm output clause" }; crate::expr::expect_token(tokens, anchor, TokenType::Lparen, "'('", context)?; let reg = register(tokens, context)?; crate::expr::expect_token(tokens, anchor, TokenType::Rparen, "')'", context)?; @@ -199,12 +170,7 @@ where inputs.push((reg, value)); } else { if !is_assignable(&value) { - return Err(ParseError::expected_at( - operand, - anchor, - "assignable expression", - context, - )); + return Err(ParseError::expected_at(operand, anchor, "assignable expression", context)); } outputs.push((reg, value)); } diff --git a/front/parser/src/parser/control.rs b/front/parser/src/parser/control.rs index 7191982a..86cfd142 100644 --- a/front/parser/src/parser/control.rs +++ b/front/parser/src/parser/control.rs @@ -34,7 +34,7 @@ fn skip_ws_and_newlines(tokens: &mut Peekable>) { match t.token_type { TokenType::Whitespace | TokenType::Newline => { tokens.next(); - } + }, _ => break, } } @@ -60,11 +60,7 @@ fn parse_match_pattern( tokens.next(); skip_ws_and_newlines(tokens); let token = tokens.peek().copied(); - let Some(Token { - token_type: TokenType::IntLiteral(value), - .. - }) = token - else { + let Some(Token { token_type: TokenType::IntLiteral(value), .. }) = token else { return Err(ParseError::expected_at( token, anchor, @@ -74,11 +70,11 @@ fn parse_match_pattern( }; tokens.next(); MatchPattern::Int(format!("-{value}")) - } + }, Some(TokenType::IntLiteral(value)) => { tokens.next(); MatchPattern::Int(value.clone()) - } + }, Some(TokenType::Identifier(name)) => { tokens.next(); if name == "_" { @@ -87,10 +83,8 @@ fn parse_match_pattern( let mut segments = vec![name.clone()]; loop { skip_ws_and_newlines(tokens); - if !matches!( - tokens.peek().map(|t| &t.token_type), - Some(TokenType::DoubleColon) - ) { + if !matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::DoubleColon)) + { break; } tokens.next(); @@ -108,10 +102,7 @@ fn parse_match_pattern( let variant_type = segments.join("::"); let mut payloads = Vec::new(); skip_ws_and_newlines(tokens); - if matches!( - tokens.peek().map(|t| &t.token_type), - Some(TokenType::Lparen) - ) { + if matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::Lparen)) { tokens.next(); loop { skip_ws_and_newlines(tokens); @@ -127,11 +118,11 @@ fn parse_match_pattern( match tokens.peek().map(|t| &t.token_type) { Some(TokenType::Comma) => { tokens.next(); - } + }, Some(TokenType::Rparen) => { tokens.next(); break; - } + }, _ => { return Err(ParseError::expected_at( tokens.peek().copied(), @@ -139,18 +130,14 @@ fn parse_match_pattern( "',' or ')'", "variant pattern", )) - } + }, } } } - MatchPattern::Variant { - variant_type, - case_name, - payloads, - } + MatchPattern::Variant { variant_type, case_name, payloads } } } - } + }, _ => { return Err(ParseError::expected_at( anchor, @@ -158,7 +145,7 @@ fn parse_match_pattern( "integer literal, case name, or '_'", "match pattern", )) - } + }, }; Ok(value.with_span(lexer::consumed_span(before, tokens))) } @@ -174,12 +161,7 @@ fn expect_header_token( tokens.next(); Ok(()) } else { - Err(ParseError::expected_at( - tokens.peek().copied(), - anchor, - spelling, - context, - )) + Err(ParseError::expected_at(tokens.peek().copied(), anchor, spelling, context)) } } @@ -212,15 +194,9 @@ pub fn parse_if(tokens: &mut Peekable>) -> Result>) -> Result>) -> bool { let mut look = tokens.clone(); - matches!( - look.next().map(|t| &t.token_type), - Some(TokenType::Identifier(_)) - ) && matches!(look.next().map(|t| &t.token_type), Some(TokenType::Colon)) + matches!(look.next().map(|t| &t.token_type), Some(TokenType::Identifier(_))) + && matches!(look.next().map(|t| &t.token_type), Some(TokenType::Colon)) } fn parse_typed_for_initializer( @@ -255,28 +229,17 @@ fn parse_typed_for_initializer( ) -> Result { let anchor = tokens.peek().copied(); let name = match tokens.peek().copied() { - Some(Token { - token_type: TokenType::Identifier(name), - .. - }) => name.clone(), + Some(Token { token_type: TokenType::Identifier(name), .. }) => name.clone(), other => { - return Err(ParseError::expected_at( - other, - anchor, - "identifier", - "for initializer", - )) - } + return Err(ParseError::expected_at(other, anchor, "identifier", "for initializer")) + }, }; tokens.next(); expect_header_token(tokens, anchor, TokenType::Colon, "':'", "for initializer")?; let type_name = parse_type_from_stream(tokens).ok_or_else(|| { ParseError::expected_at(tokens.peek().copied(), anchor, "type", "for initializer") })?; - let initial_value = if tokens - .peek() - .is_some_and(|token| token.token_type == TokenType::Equal) - { + let initial_value = if tokens.peek().is_some_and(|token| token.token_type == TokenType::Equal) { tokens.next(); Some(header_expression(tokens, "for initializer")?) } else { @@ -299,17 +262,17 @@ fn parse_for_initializer(tokens: &mut Peekable>) -> Result { tokens.next(); // consume `var` parse_typed_for_initializer(tokens, Mutability::Var) - } + }, Some(TokenType::Const | TokenType::Static) => Err( crate::parser::stmt::local_storage_error(tokens.peek().copied(), "for initializer"), ), _ if is_typed_for_initializer(tokens) => { parse_typed_for_initializer(tokens, Mutability::Var) - } + }, _ => { let expr = header_expression(tokens, "for initializer")?; Ok(ASTNode::Statement(StatementNode::Expression(expr))) - } + }, } })(); result.map(|value: ASTNode| { @@ -322,13 +285,7 @@ pub fn parse_for(tokens: &mut Peekable>) -> Result>) -> Result>) -> Result { depth -= 1; if depth == 0 { @@ -50,8 +50,8 @@ where } inner.push('>'); continue; - } - _ => {} + }, + _ => {}, } let text: &str = if !t.lexeme.is_empty() { @@ -67,14 +67,14 @@ where '<' => { depth += 1; inner.push('<'); - } + }, '>' => { depth -= 1; if depth == 0 { return Some(inner); } inner.push('>'); - } + }, _ => inner.push(ch), } } @@ -89,14 +89,11 @@ pub fn parse_const_decl(tokens: &mut Peekable>) -> Result name.clone(), + Some(Token { token_type: TokenType::Identifier(name), .. }) => name.clone(), _ => { println!("Expected identifier after `const`"); return Err(invalid(tokens.peek().copied())); - } + }, }; skip_ws(tokens); @@ -110,15 +107,11 @@ pub fn parse_const_decl(tokens: &mut Peekable>) -> Result { println!("Expected a valid type after ':'"); return Err(invalid(tokens.peek().copied())); - } + }, }; skip_ws(tokens); - let initial_value = if let Some(Token { - token_type: TokenType::Equal, - .. - }) = tokens.peek() - { + let initial_value = if let Some(Token { token_type: TokenType::Equal, .. }) = tokens.peek() { tokens.next(); // consume '=' let expr = parse_expression(tokens)?; Some(expr) @@ -132,10 +125,9 @@ pub fn parse_const_decl(tokens: &mut Peekable>) -> Result>) -> Result name.clone(), + Some(Token { token_type: TokenType::Identifier(name), .. }) => name.clone(), _ => { println!("Expected identifier"); return Err(invalid(tokens.peek().copied())); - } + }, }; skip_ws(tokens); @@ -202,15 +191,11 @@ pub fn parse_var(tokens: &mut Peekable>) -> Result { println!("Expected a valid type after ':'"); return Err(invalid(tokens.peek().copied())); - } + }, }; skip_ws(tokens); - let initial_value = if let Some(Token { - token_type: TokenType::Equal, - .. - }) = tokens.peek() - { + let initial_value = if let Some(Token { token_type: TokenType::Equal, .. }) = tokens.peek() { tokens.next(); // consume '=' let expr = parse_expression(tokens)?; Some(expr) @@ -224,10 +209,9 @@ pub fn parse_var(tokens: &mut Peekable>) -> Result>) { match t.token_type { TokenType::Whitespace | TokenType::Newline => { tokens.next(); - } + }, _ => break, } } @@ -275,28 +259,18 @@ pub(super) fn parse_ffi_header( ) -> Option<(String, Option)> { skip_ws(tokens); - if !expect( - tokens, - TokenType::Lparen, - &format!("Expected '(' after '{}'", keyword), - ) { + if !expect(tokens, TokenType::Lparen, &format!("Expected '(' after '{}'", keyword)) { return None; } skip_ws(tokens); let abi = match tokens.next() { - Some(Token { - token_type: TokenType::Identifier(name), - .. - }) => name.clone(), + Some(Token { token_type: TokenType::Identifier(name), .. }) => name.clone(), other => { - println!( - "Error: Expected ABI identifier in {}(...), found {:?}", - keyword, other - ); + println!("Error: Expected ABI identifier in {}(...), found {:?}", keyword, other); return None; - } + }, }; skip_ws(tokens); @@ -308,27 +282,22 @@ pub(super) fn parse_ffi_header( skip_ws(tokens); global_symbol = match tokens.next() { - Some(Token { - token_type: TokenType::String(s), - .. - }) => Some(String::from_utf8(s.clone()).ok()?), + Some(Token { token_type: TokenType::String(s), .. }) => { + Some(String::from_utf8(s.clone()).ok()?) + }, other => { println!( "Error: Expected string literal after ',' in {}(...), found {:?}", keyword, other ); return None; - } + }, }; skip_ws(tokens); } - if !expect( - tokens, - TokenType::Rparen, - &format!("Expected ')' to close {}(...)", keyword), - ) { + if !expect(tokens, TokenType::Rparen, &format!("Expected ')' to close {}(...)", keyword)) { return None; } @@ -346,7 +315,7 @@ fn peek_non_ws_token_type(tokens: &Peekable>) -> Option { it.next(); - } + }, _ => return Some(t.token_type.clone()), } } @@ -371,37 +340,27 @@ fn parse_extern_fun_decl( // 'fun' match tokens.peek() { - Some(Token { - token_type: TokenType::Fun, - .. - }) => { + Some(Token { token_type: TokenType::Fun, .. }) => { tokens.next(); - } + }, _ => { return Err(invalid(tokens.peek().copied())); - } + }, } skip_ws(tokens); // name let name = match tokens.next() { - Some(Token { - token_type: TokenType::Identifier(n), - .. - }) => n.clone(), + Some(Token { token_type: TokenType::Identifier(n), .. }) => n.clone(), _ => { return Err(invalid(tokens.peek().copied())); - } + }, }; skip_ws(tokens); - if !expect( - tokens, - TokenType::Lparen, - "Expected '(' after extern function name", - ) { + if !expect(tokens, TokenType::Lparen, "Expected '(' after extern function name") { return Err(invalid(tokens.peek().copied())); } @@ -420,10 +379,7 @@ fn parse_extern_fun_decl( let mut lookahead = tokens.clone(); let is_variadic = (0..3).all(|_| { - matches!( - lookahead.next().map(|token| &token.token_type), - Some(TokenType::Dot) - ) + matches!(lookahead.next().map(|token| &token.token_type), Some(TokenType::Dot)) }); if is_variadic { if params.is_empty() { @@ -446,10 +402,7 @@ fn parse_extern_fun_decl( // named param? (Identifier ... :) let is_named = match tokens.peek() { - Some(Token { - token_type: TokenType::Identifier(_), - .. - }) => { + Some(Token { token_type: TokenType::Identifier(_), .. }) => { let _next_ty = peek_non_ws_token_type(tokens); if let Some(TokenType::Identifier(_)) = tokens.peek().map(|t| t.token_type.clone()) { @@ -459,7 +412,7 @@ fn parse_extern_fun_decl( match t.token_type { TokenType::Whitespace | TokenType::Newline => { la.next(); - } + }, _ => break, } } @@ -467,21 +420,18 @@ fn parse_extern_fun_decl( } else { false } - } + }, _ => false, }; if is_named { // name : - let param_name = if let Some(Token { - token_type: TokenType::Identifier(n), - .. - }) = tokens.next() - { - n.clone() - } else { - unreachable!(); - }; + let param_name = + if let Some(Token { token_type: TokenType::Identifier(n), .. }) = tokens.next() { + n.clone() + } else { + unreachable!(); + }; skip_ws(tokens); @@ -513,14 +463,14 @@ fn parse_extern_fun_decl( Some(TokenType::Comma) => { tokens.next(); continue; - } + }, Some(TokenType::Rparen) => { tokens.next(); break; - } + }, _ => { return Err(invalid(tokens.peek().copied())); - } + }, } } @@ -533,7 +483,7 @@ fn parse_extern_fun_decl( skip_ws(tokens); crate::parser::types::parse_type_checked(tokens, "extern return type")? - } + }, _ => WaveType::Void, }; @@ -541,11 +491,7 @@ fn parse_extern_fun_decl( // per-function symbol: optional string literal let mut symbol: Option = None; - if let Some(Token { - token_type: TokenType::String(s), - .. - }) = tokens.peek() - { + if let Some(Token { token_type: TokenType::String(s), .. }) = tokens.peek() { let s = String::from_utf8(s.clone()).map_err(|_| { ParseError::expected_at( tokens.peek().copied(), @@ -565,33 +511,17 @@ fn parse_extern_fun_decl( skip_ws(tokens); - if !expect( - tokens, - TokenType::SemiColon, - "Expected ';' after extern function declaration", - ) { + if !expect(tokens, TokenType::SemiColon, "Expected ';' after extern function declaration") { return Err(invalid(tokens.peek().copied())); } - Ok(ExternFunctionNode { - name, - abi, - symbol, - params, - variadic, - return_type, - }) + Ok(ExternFunctionNode { name, abi, symbol, params, variadic, return_type }) } pub fn parse_extern(tokens: &mut Peekable>) -> Result, ParseError> { let anchor = tokens.peek().copied(); let invalid = |token| { - ParseError::expected_at( - token, - anchor, - "extern function or block", - "extern declaration", - ) + ParseError::expected_at(token, anchor, "extern function or block", "extern declaration") }; let (abi, global_symbol) = parse_extern_header(tokens).ok_or_else(|| invalid(tokens.peek().copied()))?; @@ -611,17 +541,17 @@ pub fn parse_extern(tokens: &mut Peekable>) -> Result { tokens.next(); // consume '}' break; - } + }, Some(TokenType::Fun) => { let ef = parse_extern_fun_decl(tokens, abi.clone(), global_symbol.as_ref())?; nodes.push(ASTNode::ExternFunction(ef)); - } + }, Some(TokenType::Whitespace) | Some(TokenType::Newline) => { tokens.next(); - } + }, _ => { return Err(invalid(tokens.peek().copied())); - } + }, } } @@ -646,11 +576,7 @@ pub fn parse_type_alias(tokens: &mut Peekable>) -> Result>) -> Result { @@ -661,16 +587,10 @@ pub fn parse_enum(tokens: &mut Peekable>) -> Result>) -> Result { tokens.next(); - } - Some(TokenType::Rbrace) => {} + }, + Some(TokenType::Rbrace) => {}, _ => { return Err(ParseError::expected_at( tokens.peek().copied(), @@ -703,16 +623,11 @@ pub fn parse_enum(tokens: &mut Peekable>) -> Result>) -> Result { @@ -726,10 +641,7 @@ pub fn parse_variant(tokens: &mut Peekable>) -> Result>) -> Result>) -> Result { tokens.next(); - } - Some(TokenType::Rbrace) => {} + }, + Some(TokenType::Rbrace) => {}, _ => { return Err(ParseError::expected_at( tokens.peek().copied(), @@ -791,7 +692,7 @@ pub fn parse_variant(tokens: &mut Peekable>) -> Result { tokens.next(); // consume ',' - } + }, Some(TokenType::Rparen) => continue, _ => { - println!( - "❌ Unexpected token in function arguments: {:?}", - tokens.peek() - ); + println!("❌ Unexpected token in function arguments: {:?}", tokens.peek()); return Err(invalid(tokens.peek().copied())); - } + }, } } - Ok(Expression::FunctionCall { - name, - type_args: Vec::new(), - args, - }) + Ok(Expression::FunctionCall { name, type_args: Vec::new(), args }) } pub fn parse_parentheses(tokens: &mut Peekable>) -> Vec { @@ -85,8 +78,8 @@ pub fn parse_parentheses(tokens: &mut Peekable>) -> Vec { if paren_depth == 0 { break; } - } - _ => {} + }, + _ => {}, } param_tokens.push(token.clone()); } diff --git a/front/parser/src/parser/functions.rs b/front/parser/src/parser/functions.rs index 5b913c0c..5c3b9261 100644 --- a/front/parser/src/parser/functions.rs +++ b/front/parser/src/parser/functions.rs @@ -38,10 +38,7 @@ pub fn parse_generic_param_names( tokens: &mut Peekable>, ) -> Result, ParseError> { skip_ws(tokens); - if !tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Lchevr) - { + if !tokens.peek().is_some_and(|t| t.token_type == TokenType::Lchevr) { return Ok(vec![]); } let anchor = tokens.next(); @@ -50,29 +47,20 @@ pub fn parse_generic_param_names( let mut seen = HashSet::new(); loop { skip_ws(tokens); - if !params.is_empty() - && tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Rchevr) - { + if !params.is_empty() && tokens.peek().is_some_and(|t| t.token_type == TokenType::Rchevr) { tokens.next(); break; } let at = tokens.peek().copied(); let name = crate::expr::identifier(tokens, anchor, context)?; if !seen.insert(name.clone()) { - return Err( - ParseError::syntax_at(at, format!("duplicate generic parameter '{name}'")) - .with_context(context) - .with_found_token(at), - ); + return Err(ParseError::syntax_at(at, format!("duplicate generic parameter '{name}'")) + .with_context(context) + .with_found_token(at)); } params.push(name); skip_ws(tokens); - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Comma) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::Comma) { tokens.next(); } else { crate::expr::expect_token(tokens, anchor, TokenType::Rchevr, "',' or '>'", context)?; @@ -91,10 +79,7 @@ pub fn parse_parameters( let mut names = HashSet::new(); loop { skip_ws(tokens); - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Rparen) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::Rparen) { tokens.next(); break; } @@ -102,30 +87,20 @@ pub fn parse_parameters( let at = tokens.peek().copied(); let name = crate::expr::identifier(tokens, anchor, context)?; if !names.insert(name.clone()) { - return Err( - ParseError::syntax_at(at, format!("duplicate parameter '{name}'")) - .with_context(context), - ); + return Err(ParseError::syntax_at(at, format!("duplicate parameter '{name}'")) + .with_context(context)); } crate::expr::expect_token(tokens, anchor, TokenType::Colon, "':'", context)?; skip_ws(tokens); let param_type = crate::types::parse_type_checked(tokens, "parameter type")?; skip_ws(tokens); - let initial_value = if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Equal) - { + let initial_value = if tokens.peek().is_some_and(|t| t.token_type == TokenType::Equal) { tokens.next(); skip_ws(tokens); let at = tokens.peek().copied(); let value = crate::expr::parse_expression(tokens)?; if !matches!(value.unspanned(), Expression::Literal(_) | Expression::Null) { - return Err(ParseError::expected_at( - at, - anchor, - "literal default value", - context, - )); + return Err(ParseError::expected_at(at, anchor, "literal default value", context)); } Some(value) } else { @@ -138,10 +113,7 @@ pub fn parse_parameters( initial_value, }); skip_ws(tokens); - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Comma) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::Comma) { tokens.next(); } else { crate::expr::expect_token(tokens, anchor, TokenType::Rparen, "',' or ')'", context)?; @@ -171,21 +143,13 @@ pub fn parse_function_with_export( .with_help("check parameter syntax, return type arrow, and function body braces") }; let before = tokens.clone(); - let is_async = tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::Async); + let is_async = tokens.peek().is_some_and(|t| t.token_type == TokenType::Async); if is_async { tokens.next(); skip_ws(tokens); crate::expr::expect_token(tokens, anchor, TokenType::Fun, "'fun'", "async function")?; } else { - crate::expr::expect_token( - tokens, - anchor, - TokenType::Fun, - "'fun'", - "function declaration", - )?; + crate::expr::expect_token(tokens, anchor, TokenType::Fun, "'fun'", "function declaration")?; } skip_ws(tokens); @@ -215,11 +179,7 @@ pub fn parse_function_with_export( skip_ws(tokens); let mut return_type_span = None; - let return_type = if let Some(Token { - token_type: TokenType::Arrow, - .. - }) = tokens.peek() - { + let return_type = if let Some(Token { token_type: TokenType::Arrow, .. }) = tokens.peek() { tokens.next(); // consume '->' let before_type = tokens.clone(); skip_ws(tokens); @@ -261,10 +221,7 @@ pub fn parse_export(tokens: &mut Peekable>) -> Result, }; let (abi, global_symbol) = parse_ffi_header(tokens, "export").ok_or_else(|| invalid(tokens.peek().copied()))?; - let export = ExportAttribute { - abi, - symbol: global_symbol, - }; + let export = ExportAttribute { abi, symbol: global_symbol }; skip_ws(tokens); @@ -284,7 +241,7 @@ pub fn parse_export(tokens: &mut Peekable>) -> Result, Some(TokenType::Rbrace) => { tokens.next(); break; - } + }, Some(TokenType::Fun | TokenType::Async) => { let node = parse_function_with_export(tokens, Some(export.clone()))?; if let ASTNode::Function(func) = &node { @@ -294,14 +251,14 @@ pub fn parse_export(tokens: &mut Peekable>) -> Result, } } nodes.push(node); - } + }, Some(TokenType::Whitespace) | Some(TokenType::Newline) => { tokens.next(); - } + }, other => { println!("Error: Unexpected token in export block: {:?}", other); return Err(invalid(tokens.peek().copied())); - } + }, } } diff --git a/front/parser/src/parser/io.rs b/front/parser/src/parser/io.rs index 4dcebf76..e42e621d 100644 --- a/front/parser/src/parser/io.rs +++ b/front/parser/src/parser/io.rs @@ -35,16 +35,13 @@ pub fn parse_println(tokens: &mut Peekable>) -> Result>) -> Result>) -> Result>) -> Result>) -> Result>) -> Result>) -> Result { @@ -207,10 +173,7 @@ pub fn parse_input(tokens: &mut Peekable>) -> Result>) -> Result>) -> Result>) { match t.token_type { TokenType::Whitespace | TokenType::Newline => { tokens.next(); - } + }, _ => break, } } @@ -43,17 +43,8 @@ pub fn parse_import(tokens: &mut Peekable>) -> Result>) -> Result>) -> Result>) -> Result name.clone(), + Some(Token { token_type: TokenType::Identifier(name), .. }) => name.clone(), other => { - println!( - "Error: Expected struct name after 'proto', found {:?}", - other - ); + println!("Error: Expected struct name after 'proto', found {:?}", other); return Err(invalid(tokens.peek().copied())); - } + }, }; if tokens.peek().ok_or_else(|| invalid(None))?.token_type != TokenType::Lbrace { - println!( - "Error: Expected '{{' after proto target '{}'", - target_struct - ); + println!("Error: Expected '{{' after proto target '{}'", target_struct); return Err(invalid(tokens.peek().copied())); } tokens.next(); // consume '{' @@ -155,10 +128,7 @@ pub fn parse_proto(tokens: &mut Peekable>) -> Result>) -> Result { tokens.next(); break; - } + }, TokenType::Fun | TokenType::Async => { if let ASTNode::Function(mut func_node) = parse_function(tokens)? { @@ -175,42 +145,30 @@ pub fn parse_proto(tokens: &mut Peekable>) -> Result { tokens.next(); - } + }, other => { println!("Error: Unexpected token inside proto body: {:?}", other); return Err(invalid(tokens.peek().copied())); - } + }, } } - Ok(ASTNode::ProtoImpl(ProtoImplNode { - target: target_struct, - methods, - })) + Ok(ASTNode::ProtoImpl(ProtoImplNode { target: target_struct, methods })) } pub fn parse_struct(tokens: &mut Peekable>) -> Result { let anchor = tokens.peek().copied(); let name = crate::expr::identifier(tokens, anchor, "struct name")?; let generic_params = parse_generic_param_names(tokens)?; - crate::expr::expect_token( - tokens, - anchor, - TokenType::Lbrace, - "'{'", - "struct declaration", - )?; + crate::expr::expect_token(tokens, anchor, TokenType::Lbrace, "'{'", "struct declaration")?; let mut fields = Vec::new(); let mut field_spans = Vec::new(); let mut methods = Vec::new(); @@ -220,16 +178,14 @@ pub fn parse_struct(tokens: &mut Peekable>) -> Result { tokens.next(); break; - } + }, Some(TokenType::Fun | TokenType::Async) => { - let ASTNode::Function(mut method) = parse_function(tokens)? else { - unreachable!() - }; + let ASTNode::Function(mut method) = parse_function(tokens)? else { unreachable!() }; if method.return_type.is_none() { method.return_type = Some(WaveType::Void); } methods.push(method); - } + }, Some(_) => { let before = tokens.clone(); let field_name = crate::expr::identifier(tokens, anchor, "struct field name")?; @@ -245,15 +201,8 @@ pub fn parse_struct(tokens: &mut Peekable>) -> Result { - return Err(ParseError::expected_at( - None, - anchor, - "'}'", - "struct declaration", - )) - } + }, + None => return Err(ParseError::expected_at(None, anchor, "'}'", "struct declaration")), } } Ok(ASTNode::Struct(StructNode { diff --git a/front/parser/src/parser/parse.rs b/front/parser/src/parser/parse.rs index 0cef5e85..88fa3210 100644 --- a/front/parser/src/parser/parse.rs +++ b/front/parser/src/parser/parse.rs @@ -77,14 +77,11 @@ impl ParseError { expected: &str, context: &str, ) -> Self { - Self::syntax_at( - token.or(anchor), - format!("expected {expected} in {context}"), - ) - .with_expected(expected) - .with_context(context) - .with_found("end of file") - .with_found_token(token) + Self::syntax_at(token.or(anchor), format!("expected {expected} in {context}")) + .with_expected(expected) + .with_context(context) + .with_found("end of file") + .with_found_token(token) } pub fn semantic(message: impl Into) -> Self { @@ -199,10 +196,7 @@ impl ParseError { return self; } // Legacy leaf parsers may already have consumed the EOF sentinel. - if let Some(eof) = tokens - .last() - .filter(|token| token.token_type == TokenType::Eof) - { + if let Some(eof) = tokens.last().filter(|token| token.token_type == TokenType::Eof) { self = self.with_found_token(Some(eof)); } let mut openers: Vec<&Token> = Vec::new(); @@ -221,8 +215,8 @@ impl ParseError { ) { return self; } - } - _ => {} + }, + _ => {}, } } if let Some(opener) = openers.last() { @@ -234,10 +228,9 @@ impl ParseError { }; let message = format!("unclosed '{open}' opened here; expected '{close}'"); if let Some(span) = &opener.span { - self.diag_mut().related.push(error::RelatedDiagnostic { - message, - span: span.clone(), - }); + self.diag_mut() + .related + .push(error::RelatedDiagnostic { message, span: span.clone() }); } else { self = self.with_note(format!("{message} (line {})", opener.line)); } @@ -327,11 +320,11 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { TokenType::Whitespace | TokenType::Newline => { iter.next(); continue; - } + }, TokenType::Import => { iter.next(); nodes.push(parse_import(&mut iter)?); - } + }, TokenType::Pub => { let anchor = (*token).clone(); iter.next(); @@ -346,7 +339,7 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { Some(TokenType::Import) => { iter.next(); Some(parse_import(&mut iter)?) - } + }, Some(TokenType::Export) => { iter.next(); let mut declarations = parse_export(&mut iter)?; @@ -355,32 +348,32 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { } else { None } - } + }, Some(TokenType::Fun | TokenType::Async) => Some(parse_function(&mut iter)?), Some(TokenType::Struct) => { iter.next(); Some(parse_struct(&mut iter)?) - } + }, Some(TokenType::Type) => { iter.next(); Some(parse_type_alias(&mut iter)?) - } + }, Some(TokenType::Enum) => { iter.next(); Some(parse_enum(&mut iter)?) - } + }, Some(TokenType::Variant) => { iter.next(); Some(parse_variant(&mut iter)?) - } + }, Some(TokenType::Const) => { iter.next(); Some(parse_const(&mut iter)?) - } + }, Some(TokenType::Static) => { iter.next(); Some(parse_static(&mut iter)?) - } + }, _ => None, }; @@ -417,7 +410,7 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { .with_help("select the public symbols that this module re-exports")); } import.visibility = Visibility::Public; - } + }, ASTNode::Function(function) => { if function.name == "main" { return Err(ParseError::syntax_at( @@ -429,7 +422,7 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { .with_help("remove `pub`; `main` is a private program entry point")); } function.visibility = Visibility::Public; - } + }, ASTNode::Struct(structure) => structure.visibility = Visibility::Public, ASTNode::TypeAlias(alias) => alias.visibility = Visibility::Public, ASTNode::Enum(enumeration) => enumeration.visibility = Visibility::Public, @@ -438,62 +431,60 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { _ => unreachable!("public parser only constructs importable declarations"), } nodes.push(declaration); - } + }, TokenType::Extern => { iter.next(); nodes.extend(parse_extern(&mut iter)?); - } + }, TokenType::Export => { iter.next(); let export_nodes = parse_export(&mut iter)?; nodes.extend(export_nodes); - } + }, TokenType::Const => { iter.next(); nodes.push(parse_const(&mut iter)?); - } + }, TokenType::Static => { iter.next(); nodes.push(parse_static(&mut iter)?); - } + }, TokenType::Proto => { iter.next(); nodes.push(parse_proto(&mut iter)?); - } + }, TokenType::Type => { iter.next(); nodes.push(parse_type_alias(&mut iter)?); - } + }, TokenType::Enum => { iter.next(); nodes.push(parse_enum(&mut iter)?); - } + }, TokenType::Variant => { iter.next(); nodes.push(parse_variant(&mut iter)?); - } + }, TokenType::Struct => { iter.next(); let struct_node = parse_struct(&mut iter)?; nodes.push(struct_node); - } + }, TokenType::Fun | TokenType::Async => { let func = parse_function(&mut iter)?; nodes.push(func); - } + }, TokenType::Eof => break, _ => { - return Err( - ParseError::syntax_at(Some(token), "unexpected token at top level") - .with_context("top-level items") - .with_expected_many([ - "import", "extern", "pub", "const", "static", "type", "enum", - "variant", "struct", "proto", "fun", "export", - ]) - .with_found_token(Some(token)) - .with_help("only declarations are allowed at top level"), - ); - } + return Err(ParseError::syntax_at(Some(token), "unexpected token at top level") + .with_context("top-level items") + .with_expected_many([ + "import", "extern", "pub", "const", "static", "type", "enum", "variant", + "struct", "proto", "fun", "export", + ]) + .with_found_token(Some(token)) + .with_help("only declarations are allowed at top level")); + }, } for node in &mut nodes[first_node..] { let value = std::mem::replace(node, ASTNode::Expression(crate::ast::Expression::Null)); @@ -508,10 +499,7 @@ fn parse_syntax_impl(tokens: &[Token]) -> Result, ParseError> { fn validate_explicit_variable_types(tokens: &[Token]) -> Result<(), ParseError> { let next_significant = |start: usize| { (start..tokens.len()).find(|index| { - !matches!( - tokens[*index].token_type, - TokenType::Whitespace | TokenType::Newline - ) + !matches!(tokens[*index].token_type, TokenType::Whitespace | TokenType::Newline) }) }; diff --git a/front/parser/src/parser/stmt.rs b/front/parser/src/parser/stmt.rs index 4c6e0f62..3120c0d0 100644 --- a/front/parser/src/parser/stmt.rs +++ b/front/parser/src/parser/stmt.rs @@ -44,7 +44,7 @@ pub fn parse_block(tokens: &mut Peekable>) -> Result, P Some(TokenType::Rbrace) => { tokens.next(); return Ok(body); - } + }, None | Some(TokenType::Eof) => { return Err(ParseError::expected_at( tokens.peek().copied(), @@ -52,7 +52,7 @@ pub fn parse_block(tokens: &mut Peekable>) -> Result, P "'}'", "block", )); - } + }, _ => body.push(parse_statement(tokens)?), } } @@ -65,19 +65,19 @@ pub fn parse_statement(tokens: &mut Peekable>) -> Result { tokens.next(); parse_if(tokens) - } + }, Some(TokenType::For) => { tokens.next(); parse_for(tokens) - } + }, Some(TokenType::While) => { tokens.next(); parse_while(tokens) - } + }, Some(TokenType::Match) => { tokens.next(); parse_match(tokens) - } + }, _ => parse_simple_statement(tokens), }; result.map(|value: ASTNode| { @@ -100,38 +100,35 @@ fn parse_simple_statement(tokens: &mut Peekable>) -> Result { tokens.next(); parse_var(tokens) - } + }, TokenType::Let | TokenType::Mut => { println!("Error: `let` and `let mut` declarations were removed; use `var`"); Err(invalid(tokens.peek().copied())) - } - TokenType::Const | TokenType::Static => Err(local_storage_error( - tokens.peek().copied(), - "block statement", - )), + }, + TokenType::Const | TokenType::Static => { + Err(local_storage_error(tokens.peek().copied(), "block statement")) + }, TokenType::Println => { tokens.next(); parse_println(tokens) - } + }, TokenType::Print => { tokens.next(); parse_print(tokens) - } + }, TokenType::Input => { tokens.next(); parse_input(tokens) - } + }, TokenType::Continue | TokenType::Break => { tokens.next(); semicolon(tokens).ok_or_else(|| invalid(tokens.peek().copied()))?; - Ok(ASTNode::Statement( - if token.token_type == TokenType::Continue { - StatementNode::Continue - } else { - StatementNode::Break - }, - )) - } + Ok(ASTNode::Statement(if token.token_type == TokenType::Continue { + StatementNode::Continue + } else { + StatementNode::Break + })) + }, TokenType::Return => { tokens.next(); let expr = @@ -142,18 +139,15 @@ fn parse_simple_statement(tokens: &mut Peekable>) -> Result { tokens.next(); let node = crate::parser::asm::parse_asm_block(tokens)?; - if tokens - .peek() - .is_some_and(|t| t.token_type == TokenType::SemiColon) - { + if tokens.peek().is_some_and(|t| t.token_type == TokenType::SemiColon) { tokens.next(); } Ok(node) - } + }, TokenType::Rbrace => Err(invalid(tokens.peek().copied())), _ => { @@ -168,17 +162,14 @@ fn parse_simple_statement(tokens: &mut Peekable>) -> Result, context: &str) -> ParseError { - ParseError::syntax_at( - token, - "`const` and `static` declarations are only allowed at top level", - ) - .with_context(context) - .with_expected("var declaration or expression") - .with_found_token(token) - .with_help("use `var` for local storage, or move the declaration to top level") + ParseError::syntax_at(token, "`const` and `static` declarations are only allowed at top level") + .with_context(context) + .with_expected("var declaration or expression") + .with_found_token(token) + .with_help("use `var` for local storage, or move the declaration to top level") } diff --git a/front/parser/src/parser/types.rs b/front/parser/src/parser/types.rs index 6ac41267..51e276cd 100644 --- a/front/parser/src/parser/types.rs +++ b/front/parser/src/parser/types.rs @@ -34,7 +34,7 @@ pub fn split_top_level_generic_args(inner: &str) -> Option> { if depth < 0 { return None; } - } + }, ',' if depth == 0 => { let part = inner[start..i].trim(); if part.is_empty() { @@ -42,8 +42,8 @@ pub fn split_top_level_generic_args(inner: &str) -> Option> { } parts.push(part.to_string()); start = i + 1; - } - _ => {} + }, + _ => {}, } } @@ -99,20 +99,18 @@ pub fn token_type_to_wave_type(token_type: &TokenType) -> Option { TokenType::TypeString => Some(WaveType::String), TokenType::TypePointer(inner) => { token_type_to_wave_type(inner).map(|t| WaveType::Pointer(Box::new(t))) - } + }, TokenType::TypeArray(inner, size) => { token_type_to_wave_type(inner).map(|t| WaveType::Array(Box::new(t), *size)) - } + }, TokenType::TypeCustom(name) if name.starts_with("Future<") => { let inner = name.strip_prefix("Future<")?.strip_suffix('>')?; let args = split_top_level_generic_args(inner)?; if args.len() != 1 { return None; } - Some(WaveType::Future(Box::new(token_type_to_wave_type( - &parse_type(&args[0])?, - )?))) - } + Some(WaveType::Future(Box::new(token_type_to_wave_type(&parse_type(&args[0])?)?))) + }, TokenType::TypeCustom(name) => Some(WaveType::Struct(name.clone())), _ => None, } @@ -198,7 +196,7 @@ pub fn parse_type(type_str: &str) -> Option { "char" => return Some(TokenType::TypeChar), "byte" => return Some(TokenType::TypeByte), "str" => return Some(TokenType::TypeString), - _ => {} + _ => {}, } if let Some(prefix @ ('i' | 'u' | 'f')) = type_str.chars().next() { let suffix = &type_str[1..]; @@ -210,10 +208,10 @@ pub fn parse_type(type_str: &str) -> Option { return match prefix { 'i' if matches!(bits, 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024) => { Some(TokenType::TypeInt(bits)) - } + }, 'u' if matches!(bits, 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024) => { Some(TokenType::TypeUint(bits)) - } + }, 'f' if matches!(bits, 32 | 64) => Some(TokenType::TypeFloat(bits)), _ => None, }; @@ -222,10 +220,7 @@ pub fn parse_type(type_str: &str) -> Option { if type_str.split("::").all(|segment| { !segment.is_empty() - && segment - .chars() - .next() - .is_some_and(|c| c.is_alphabetic() || c == '_') + && segment.chars().next().is_some_and(|c| c.is_alphabetic() || c == '_') && segment.chars().all(|c| c.is_alphanumeric() || c == '_') }) { return Some(TokenType::TypeCustom(type_str.to_string())); @@ -244,10 +239,10 @@ pub fn validate_type(expected: &TokenType, actual: &TokenType) -> bool { (TokenType::TypeByte, TokenType::TypeByte) => true, (TokenType::TypePointer(inner1), TokenType::TypePointer(inner2)) => { validate_type(&**inner1, &**inner2) // Double dereference to get TokenType - } + }, (TokenType::TypeArray(inner1, size1), TokenType::TypeArray(inner2, size2)) => { validate_type(&**inner1, &**inner2) && size1 == size2 // Double dereference to get TokenType - } + }, (TokenType::TypeString, TokenType::TypeString) => true, _ => false, } @@ -280,10 +275,7 @@ pub fn parse_type_from_stream<'a, T>(tokens: &mut Peekable) -> Option, { - while matches!( - tokens.peek().map(|t| &t.token_type), - Some(TokenType::Whitespace) - ) { + while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::Whitespace)) { tokens.next(); } @@ -291,16 +283,13 @@ where if let TokenType::Identifier(name) = &type_token.token_type { let mut name = name.clone(); - while matches!( - tokens.peek().map(|token| &token.token_type), - Some(TokenType::DoubleColon) - ) { + while matches!(tokens.peek().map(|token| &token.token_type), Some(TokenType::DoubleColon)) { tokens.next(); match tokens.next().map(|token| &token.token_type) { Some(TokenType::Identifier(segment)) => { name.push_str("::"); name.push_str(segment); - } + }, _ => return None, } } @@ -312,10 +301,7 @@ where tokens.next(); } - if matches!( - tokens.peek().map(|t| &t.token_type), - Some(TokenType::Lchevr) - ) { + if matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::Lchevr)) { tokens.next(); // consume '<' let inner = collect_generic_inner(tokens)?; @@ -348,9 +334,7 @@ where Some(ty) => { *tokens = probe; Ok(ty) - } - None => Err(crate::parser::ParseError::expected_at( - anchor, anchor, "type", context, - )), + }, + None => Err(crate::parser::ParseError::expected_at(anchor, anchor, "type", context)), } } diff --git a/front/parser/src/source.rs b/front/parser/src/source.rs index b01da4d5..f1ffc744 100644 --- a/front/parser/src/source.rs +++ b/front/parser/src/source.rs @@ -53,25 +53,25 @@ impl SourceMap { for f in &mut s.methods { self.function(f); } - } + }, ASTNode::ProtoImpl(p) => { for f in &mut p.methods { self.function(f); } - } + }, ASTNode::Program(p) => { if let Some(e) = &mut p.initial_value { self.expression(e); } - } + }, ASTNode::Variable(v) => { if let Some(e) = &mut v.initial_value { self.expression(e); } - } + }, ASTNode::Expression(e) => self.expression(e), ASTNode::Statement(s) => self.statement(s), - _ => {} + _ => {}, } } @@ -83,13 +83,8 @@ impl SourceMap { for e in args { self.expression(e); } - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { self.expression(condition); for node in body { self.node(node); @@ -107,26 +102,21 @@ impl SourceMap { self.node(node); } } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + }, + StatementNode::For { initialization, condition, increment, body } => { self.node(initialization); self.expression(condition); self.expression(increment); for node in body { self.node(node); } - } + }, StatementNode::While { condition, body } => { self.expression(condition); for node in body { self.node(node); } - } + }, StatementNode::Match { value, arms } => { self.expression(value); for arm in arms { @@ -135,18 +125,16 @@ impl SourceMap { self.node(node); } } - } + }, StatementNode::Assign { value, .. } | StatementNode::Return(Some(value)) | StatementNode::Expression(value) => self.expression(value), - StatementNode::AsmBlock { - inputs, outputs, .. - } => { + StatementNode::AsmBlock { inputs, outputs, .. } => { for (_, e) in inputs.iter_mut().chain(outputs.iter_mut()) { self.expression(e); } - } - _ => {} + }, + _ => {}, } } @@ -160,26 +148,25 @@ impl SourceMap { *expression = *value; } if let Some(span) = span { - self.expressions - .insert(expression as *const _ as usize, span); + self.expressions.insert(expression as *const _ as usize, span); } match expression { Expression::StructLiteral { fields, .. } => { for (_, e) in fields { self.expression(e); } - } + }, Expression::FunctionCall { args, .. } | Expression::ArrayLiteral(args) => { for e in args { self.expression(e); } - } + }, Expression::MethodCall { object, args, .. } => { self.expression(object); for e in args { self.expression(e); } - } + }, Expression::Deref(e) | Expression::AddressOf(e) | Expression::Await(e) @@ -189,30 +176,18 @@ impl SourceMap { | Expression::FieldAccess { object: e, .. } | Expression::IncDec { target: e, .. } => self.expression(e), Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } - | Expression::AssignOperation { - target: left, - value: right, - .. - } - | Expression::Assignment { - target: left, - value: right, - } => { + | Expression::IndexAccess { target: left, index: right } + | Expression::AssignOperation { target: left, value: right, .. } + | Expression::Assignment { target: left, value: right } => { self.expression(left); self.expression(right); - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, e) in inputs.iter_mut().chain(outputs.iter_mut()) { self.expression(e); } - } - _ => {} + }, + _ => {}, } } diff --git a/front/parser/src/verification.rs b/front/parser/src/verification.rs index 80d69898..1f3ddff4 100644 --- a/front/parser/src/verification.rs +++ b/front/parser/src/verification.rs @@ -84,7 +84,7 @@ fn infer_variant_type_pair( | (WaveType::Pointer(template), WaveType::Pointer(actual)) | (WaveType::Array(template, _), WaveType::Array(actual, _)) => { infer_variant_type_pair(program, template, actual, generic_params, substitutions) - } + }, (WaveType::Struct(template_name), WaveType::Struct(actual_name)) | (WaveType::Struct(template_name), WaveType::Variant(actual_name)) | (WaveType::Variant(template_name), WaveType::Struct(actual_name)) @@ -109,7 +109,7 @@ fn infer_variant_type_pair( )?; } Ok(()) - } + }, _ => Ok(()), } } @@ -177,11 +177,7 @@ impl<'a> Validator<'a> { .entry((kind.clone(), text.clone())) .and_modify(|count| *count += 1) .or_insert(1); - self.primary_span = Some(SemanticSpanHint { - kind, - text, - occurrence: *occurrence, - }); + self.primary_span = Some(SemanticSpanHint { kind, text, occurrence: *occurrence }); } fn diagnostic(&self, message: String) -> SemanticDiagnostic { @@ -240,10 +236,8 @@ impl<'a> Validator<'a> { .replace(function.return_type.clone().unwrap_or(WaveType::Void)); let previous_loop_depth = std::mem::replace(&mut self.loop_depth, 0); let previous_type_params = std::mem::take(&mut self.current_type_params); - self.current_type_params - .extend(inherited_type_params.iter().cloned()); - self.current_type_params - .extend(function.generic_params.iter().cloned()); + self.current_type_params.extend(inherited_type_params.iter().cloned()); + self.current_type_params.extend(function.generic_params.iter().cloned()); for parameter in &function.parameters { if let Some(span) = ¶meter.span { @@ -253,10 +247,7 @@ impl<'a> Validator<'a> { ¶meter.param_type, &self.current_type_params, false, - &format!( - "parameter `{}` of function `{}`", - parameter.name, display_name - ), + &format!("parameter `{}` of function `{}`", parameter.name, display_name), )?; } let mut saw_default = false; @@ -276,10 +267,7 @@ impl<'a> Validator<'a> { )); } } - self.source_span = function - .return_type_span - .clone() - .or_else(|| function.span.clone()); + self.source_span = function.return_type_span.clone().or_else(|| function.span.clone()); let return_type = function.return_type.clone().unwrap_or(WaveType::Void); self.program.validate_type( &return_type, @@ -292,10 +280,7 @@ impl<'a> Validator<'a> { for parameter in &function.parameters { validator.insert_current_binding( parameter.name.clone(), - Binding { - mutability: Mutability::Var, - ty: parameter.param_type.clone(), - }, + Binding { mutability: Mutability::Var, ty: parameter.param_type.clone() }, "parameter", )?; } @@ -330,10 +315,7 @@ impl<'a> Validator<'a> { ) -> Result<(), String> { let scope = self.scopes.last_mut().unwrap(); if scope.contains_key(&name) { - return Err(format!( - "duplicate {} declaration `{}` in the same scope", - kind, name - )); + return Err(format!("duplicate {} declaration `{}` in the same scope", kind, name)); } scope.insert(name, binding); Ok(()) @@ -378,10 +360,7 @@ impl<'a> Validator<'a> { if asm_initializer { self.insert_current_binding( variable.name.clone(), - Binding { - mutability: variable.mutability, - ty: variable.type_name.clone(), - }, + Binding { mutability: variable.mutability, ty: variable.type_name.clone() }, "variable", )?; } @@ -399,20 +378,17 @@ impl<'a> Validator<'a> { if !asm_initializer { self.insert_current_binding( variable.name.clone(), - Binding { - mutability: variable.mutability, - ty: variable.type_name.clone(), - }, + Binding { mutability: variable.mutability, ty: variable.type_name.clone() }, "variable", )?; } Ok(true) - } + }, ASTNode::Statement(statement) => self.validate_statement(statement), ASTNode::Expression(expression) => { let ty = self.validate_expr(expression)?; Ok(!matches!(ty, ExpressionType::Known(WaveType::Never))) - } + }, _ => Ok(true), } } @@ -422,7 +398,7 @@ impl<'a> Validator<'a> { StatementNode::Expression(expression) => { let ty = self.validate_expr(expression)?; Ok(!matches!(ty, ExpressionType::Known(WaveType::Never))) - } + }, StatementNode::Assign { variable, value } => { self.mark_span(SemanticSpanKind::Identifier, variable.clone()); let binding = self @@ -436,7 +412,7 @@ impl<'a> Validator<'a> { &format!("assignment to `{}`", variable), )?; Ok(true) - } + }, StatementNode::PrintFormat { args, format } | StatementNode::PrintlnFormat { args, format } => { self.mark_span(SemanticSpanKind::Keyword, "println|print"); @@ -456,24 +432,23 @@ impl<'a> Validator<'a> { )?; } let concrete = canonical_expression_type(self.program, &ty); - let generic = concrete - .as_ref() - .is_some_and(|t| self.program.is_generic_placeholder(t)); + let generic = + concrete.as_ref().is_some_and(|t| self.program.is_generic_placeholder(t)); let valid = generic || match spec { "" => true, "d" | "x" | "c" => { self.is_integer_expression(&ty) || matches!(concrete, Some(WaveType::Bool)) - } + }, "p" => { matches!(concrete, Some(WaveType::Pointer(_) | WaveType::String)) || matches!(ty, ExpressionType::Null) - } + }, "s" => { matches!(concrete, Some(WaveType::String)) || matches!(concrete, Some(WaveType::Pointer(ref t)) if matches!(t.as_ref(), WaveType::Byte | WaveType::Char)) - } + }, _ => false, }; if !valid { @@ -484,7 +459,7 @@ impl<'a> Validator<'a> { } } Ok(true) - } + }, StatementNode::Input { args, .. } => { self.mark_span(SemanticSpanKind::Keyword, "input"); for argument in args { @@ -513,13 +488,8 @@ impl<'a> Validator<'a> { } } Ok(true) - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { self.mark_span(SemanticSpanKind::Keyword, "if"); self.validate_condition(condition, "if condition")?; let mut any_branch_falls_through = self.validate_scoped_block(body)?; @@ -539,7 +509,7 @@ impl<'a> Validator<'a> { } Ok(any_branch_falls_through) - } + }, StatementNode::While { condition, body } => { self.mark_span(SemanticSpanKind::Keyword, "while"); self.validate_condition(condition, "while condition")?; @@ -549,24 +519,21 @@ impl<'a> Validator<'a> { body_result?; Ok(!expression_is_true(condition) || block_breaks_current_loop(body)) - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => self.with_scope(|validator| { - validator.mark_span(SemanticSpanKind::Keyword, "for"); - validator.validate_node(initialization)?; - validator.validate_condition(condition, "for condition")?; - validator.validate_expr(increment)?; - validator.loop_depth += 1; - let body_result = validator.validate_block(body); - validator.loop_depth -= 1; - body_result?; - - Ok(!expression_is_true(condition) || block_breaks_current_loop(body)) - }), + }, + StatementNode::For { initialization, condition, increment, body } => { + self.with_scope(|validator| { + validator.mark_span(SemanticSpanKind::Keyword, "for"); + validator.validate_node(initialization)?; + validator.validate_condition(condition, "for condition")?; + validator.validate_expr(increment)?; + validator.loop_depth += 1; + let body_result = validator.validate_block(body); + validator.loop_depth -= 1; + body_result?; + + Ok(!expression_is_true(condition) || block_breaks_current_loop(body)) + }) + }, StatementNode::Match { value, arms } => { self.mark_span(SemanticSpanKind::Keyword, "match"); let value_type = self.validate_expr(value)?; @@ -575,7 +542,7 @@ impl<'a> Validator<'a> { WaveType::Variant(name) => self.validate_variant_match(&name, arms), _ if self.is_integer_expression(&value_type) => { self.validate_integer_match(&value_type, arms) - } + }, _ => Err(format!( "match value must be an integer, enum, or variant, found `{}`", display_expression_type(&value_type) @@ -583,43 +550,38 @@ impl<'a> Validator<'a> { }, _ if self.is_integer_expression(&value_type) => { self.validate_integer_match(&value_type, arms) - } + }, _ => Err(format!( "match value must be an integer, enum, or variant, found `{}`", display_expression_type(&value_type) )), } - } + }, StatementNode::Break => { self.mark_span(SemanticSpanKind::Keyword, "break"); if self.loop_depth == 0 { return Err("`break` can only be used inside a loop".to_string()); } Ok(false) - } + }, StatementNode::Continue => { self.mark_span(SemanticSpanKind::Keyword, "continue"); if self.loop_depth == 0 { return Err("`continue` can only be used inside a loop".to_string()); } Ok(false) - } + }, StatementNode::Return(value) => { self.mark_span(SemanticSpanKind::Keyword, "return"); self.validate_return(value.as_ref())?; Ok(false) - } - StatementNode::AsmBlock { - inputs, - outputs, - clobbers, - .. - } => { + }, + StatementNode::AsmBlock { inputs, outputs, clobbers, .. } => { for (_, expression) in inputs.iter().chain(outputs.iter()) { self.validate_expr(expression)?; } Ok(!clobbers.iter().any(|clobber| clobber == "noreturn")) - } + }, _ => Ok(true), } } @@ -652,24 +614,19 @@ impl<'a> Validator<'a> { let mut all_arms_terminate = !arms.is_empty(); for arm in arms { - if let Some(span) = self - .source_map - .patterns - .get(&(&arm.pattern as *const _ as usize)) - { + if let Some(span) = self.source_map.patterns.get(&(&arm.pattern as *const _ as usize)) { self.source_span = Some(span.clone()); } let key = match &arm.pattern { MatchPattern::Located { .. } => { unreachable!("source wrappers detached before analysis") - } + }, MatchPattern::Int(raw) => { self.mark_span(SemanticSpanKind::Keyword, raw.clone()); let value = normalize(raw, &ty)?; - self.integer_patterns - .insert(&arm.pattern as *const _ as usize, value.clone()); + self.integer_patterns.insert(&arm.pattern as *const _ as usize, value.clone()); format!("value:{}", value) - } + }, MatchPattern::Ident(name) => { self.mark_span(SemanticSpanKind::Identifier, name.clone()); let binding = self @@ -684,25 +641,21 @@ impl<'a> Validator<'a> { )); } let value = self.program.constant_values.get(name).ok_or_else(|| { - format!( - "match case `{}` does not have a compile-time integer value", - name - ) + format!("match case `{}` does not have a compile-time integer value", name) })?; let value = normalize(value, &self.program.canonical_type(&binding.ty))?; let value = normalize(&value, &ty)?; - self.integer_patterns - .insert(&arm.pattern as *const _ as usize, value.clone()); + self.integer_patterns.insert(&arm.pattern as *const _ as usize, value.clone()); format!("value:{}", value) - } + }, MatchPattern::Wildcard => { self.mark_span(SemanticSpanKind::Keyword, "_"); has_wildcard = true; "wildcard:_".to_string() - } + }, MatchPattern::Binding(_) | MatchPattern::Variant { .. } => { return Err("variant patterns require a variant match value".to_string()) - } + }, }; if !seen.insert(key.clone()) { return Err(format!("duplicate match case pattern `{}`", key)); @@ -719,22 +672,15 @@ impl<'a> Validator<'a> { arms: &[crate::ast::MatchArm], ) -> Result { let definition = self.program.variant_type(variant_name).cloned().unwrap(); - let all_cases = definition - .cases - .iter() - .map(|(name, _)| name.clone()) - .collect::>(); + let all_cases = + definition.cases.iter().map(|(name, _)| name.clone()).collect::>(); let expected_type = WaveType::Variant(variant_name.to_string()); let mut seen_patterns = HashSet::new(); let mut covered_patterns: Vec<&MatchPattern> = Vec::new(); let mut all_arms_terminate = !arms.is_empty(); for arm in arms { - if let Some(span) = self - .source_map - .patterns - .get(&(&arm.pattern as *const _ as usize)) - { + if let Some(span) = self.source_map.patterns.get(&(&arm.pattern as *const _ as usize)) { self.source_span = Some(span.clone()); } if self.variant_patterns_cover(&covered_patterns, &expected_type) { @@ -754,13 +700,9 @@ impl<'a> Validator<'a> { match &arm.pattern { MatchPattern::Located { .. } => { unreachable!("source wrappers detached before analysis") - } - MatchPattern::Wildcard => {} - MatchPattern::Variant { - variant_type, - case_name, - .. - } => { + }, + MatchPattern::Wildcard => {}, + MatchPattern::Variant { variant_type, case_name, .. } => { if self.program.named_type_base(variant_type) != self.program.named_type_base(variant_name) { @@ -781,23 +723,20 @@ impl<'a> Validator<'a> { &expected_type, &mut bindings, )?; - } + }, MatchPattern::Int(_) | MatchPattern::Ident(_) | MatchPattern::Binding(_) => { return Err(format!( "match on variant `{}` requires a qualified case pattern or `_`", variant_name )) - } + }, } let arm_falls_through = self.with_scope(|validator| { for (name, ty) in bindings { validator.insert_current_binding( name, - Binding { - mutability: Mutability::Var, - ty, - }, + Binding { mutability: Mutability::Var, ty }, "pattern binding", )?; } @@ -847,19 +786,15 @@ impl<'a> Validator<'a> { match pattern { MatchPattern::Located { .. } => { unreachable!("source wrappers detached before analysis") - } + }, MatchPattern::Binding(name) => { if bindings.insert(name.clone(), expected.clone()).is_some() { return Err(format!("duplicate pattern binding `{}`", name)); } Ok(()) - } + }, MatchPattern::Wildcard => Ok(()), - MatchPattern::Variant { - variant_type, - case_name, - payloads, - } => { + MatchPattern::Variant { variant_type, case_name, payloads } => { let WaveType::Variant(expected_name) = self.program.canonical_type(expected) else { return Err(format!( "nested variant pattern `{}::{}` cannot match payload type `{}`", @@ -876,14 +811,9 @@ impl<'a> Validator<'a> { variant_type, case_name, expected_name )); } - let (discriminant, payload_types) = self - .program - .variant_case(&expected_name, case_name) - .ok_or_else(|| { - format!( - "unknown case `{}` in variant `{}`", - case_name, expected_name - ) + let (discriminant, payload_types) = + self.program.variant_case(&expected_name, case_name).ok_or_else(|| { + format!("unknown case `{}` in variant `{}`", case_name, expected_name) })?; if payloads.len() != payload_types.len() { return Err(format!( @@ -907,10 +837,10 @@ impl<'a> Validator<'a> { self.collect_variant_pattern_bindings(payload, ty, bindings)?; } Ok(()) - } + }, MatchPattern::Int(_) | MatchPattern::Ident(_) => { Err("variant payload patterns support bindings, `_`, or nested cases".to_string()) - } + }, } } @@ -918,13 +848,9 @@ impl<'a> Validator<'a> { match pattern { MatchPattern::Located { .. } => { unreachable!("source wrappers detached before analysis") - } + }, MatchPattern::Binding(_) | MatchPattern::Wildcard => true, - MatchPattern::Variant { - variant_type, - case_name, - payloads, - } => { + MatchPattern::Variant { variant_type, case_name, payloads } => { let WaveType::Variant(expected_name) = self.program.canonical_type(expected) else { return false; }; @@ -948,7 +874,7 @@ impl<'a> Validator<'a> { .iter() .zip(&payload_types) .all(|(payload, ty)| self.variant_pattern_is_irrefutable(payload, ty)) - } + }, MatchPattern::Int(_) | MatchPattern::Ident(_) => false, } } @@ -970,16 +896,13 @@ impl<'a> Validator<'a> { let case_patterns = patterns .iter() .filter_map(|pattern| match pattern { - MatchPattern::Variant { - variant_type, - case_name: pattern_case, - payloads, - } if self.program.named_type_base(variant_type) - == self.program.named_type_base(&expected_name) - && pattern_case == case_name => + MatchPattern::Variant { variant_type, case_name: pattern_case, payloads } + if self.program.named_type_base(variant_type) + == self.program.named_type_base(&expected_name) + && pattern_case == case_name => { Some(payloads) - } + }, _ => None, }) .collect::>(); @@ -1011,10 +934,8 @@ impl<'a> Validator<'a> { } fn validate_return(&mut self, value: Option<&Expression>) -> Result<(), String> { - let function = self - .current_function - .clone() - .unwrap_or_else(|| "".to_string()); + let function = + self.current_function.clone().unwrap_or_else(|| "".to_string()); let expected = self.current_return_type.clone().unwrap_or(WaveType::Void); match (expected, value) { @@ -1023,15 +944,13 @@ impl<'a> Validator<'a> { )), (WaveType::Void, None) => Ok(()), (WaveType::Void, Some(expression)) => { - if matches!( - self.validate_expr(expression)?, - ExpressionType::Known(WaveType::Void) - ) { + if matches!(self.validate_expr(expression)?, ExpressionType::Known(WaveType::Void)) + { Ok(()) } else { Err(format!("void function `{function}` cannot return a value")) } - } + }, (expected, None) => Err(format!( "non-void function `{}` must return `{}`", function, @@ -1044,7 +963,7 @@ impl<'a> Validator<'a> { &expected, &format!("return value of function `{}`", function), ) - } + }, } } @@ -1090,7 +1009,7 @@ impl<'a> Validator<'a> { | WaveType::String | WaveType::Pointer(_) ) - } + }, ExpressionType::ArrayLiteral(_) | ExpressionType::AddressedArrayLiteral(_) => false, }; if supported { @@ -1141,11 +1060,7 @@ impl<'a> Validator<'a> { expression: &Expression, expected: Option<&WaveType>, ) -> Result { - if let Some(span) = self - .source_map - .expressions - .get(&(expression as *const _ as usize)) - { + if let Some(span) = self.source_map.expressions.get(&(expression as *const _ as usize)) { self.source_span = Some(span.clone()); } @@ -1162,8 +1077,7 @@ impl<'a> Validator<'a> { analyzed_expression_type(self.program, expression_type), ); if let Some(ty) = canonical_expression_type(self.program, expression_type) { - self.expression_types - .insert(expression as *const Expression as usize, ty); + self.expression_types.insert(expression as *const Expression as usize, ty); } } result @@ -1230,9 +1144,7 @@ impl<'a> Validator<'a> { ) -> Result { if crate::layout_intrinsics::is_intrinsic(name) { if type_args.len() != 1 || !args.is_empty() { - return Err(format!( - "{name} requires one type argument and no value arguments" - )); + return Err(format!("{name} requires one type argument and no value arguments")); } self.program.validate_type( &type_args[0], @@ -1265,10 +1177,8 @@ impl<'a> Validator<'a> { return Err("async frame creation requires a generated resume function".into()); } } - let actual = args - .iter() - .map(|a| self.validate_expr(a)) - .collect::, _>>()?; + let actual = + args.iter().map(|a| self.validate_expr(a)).collect::, _>>()?; let concrete = actual .iter() .map(|a| match a { @@ -1304,10 +1214,7 @@ impl<'a> Validator<'a> { if let Some(signature) = self.program.functions.get(name).cloned() { if type_args.is_empty() && !signature.generic_params.is_empty() - && signature - .params - .iter() - .any(|t| matches!(t, WaveType::Future(_))) + && signature.params.iter().any(|t| matches!(t, WaveType::Future(_))) { let mut subst = HashMap::new(); for (template, arg) in signature.params.iter().zip(args) { @@ -1340,18 +1247,10 @@ impl<'a> Validator<'a> { ); } } - let type_args = if inferred.is_empty() { - type_args - } else { - &inferred - }; + let type_args = if inferred.is_empty() { type_args } else { &inferred }; let signature = self.program.functions.get(name).map(|signature| { - let substitutions: HashMap = signature - .generic_params - .iter() - .cloned() - .zip(type_args.iter().cloned()) - .collect(); + let substitutions: HashMap = + signature.generic_params.iter().cloned().zip(type_args.iter().cloned()).collect(); substitute_function_type(signature, &substitutions) }); let Some(signature) = signature else { @@ -1361,11 +1260,8 @@ impl<'a> Validator<'a> { return Err(format!("call to unknown function `{}`", name)); }; - let declared_generic_count = self - .program - .functions - .get(name) - .map_or(0, |function| function.generic_params.len()); + let declared_generic_count = + self.program.functions.get(name).map_or(0, |function| function.generic_params.len()); if type_args.len() != declared_generic_count { return Err(format!( "function `{}` expects {} generic argument(s), found {}", @@ -1406,15 +1302,14 @@ impl<'a> Validator<'a> { )); } - let expected_variant = expected - .map(|ty| self.program.canonical_type(ty)) - .and_then(|ty| match ty { + let expected_variant = + expected.map(|ty| self.program.canonical_type(ty)).and_then(|ty| match ty { WaveType::Variant(name) if self.program.named_type_base(&name) == self.program.named_type_base(owner) => { Some(name) - } + }, _ => None, }); @@ -1495,11 +1390,8 @@ impl<'a> Validator<'a> { ExpressionType::Known(ty) => ExpressionType::Known(self.program.canonical_type(&ty)), other => other, }; - self.source_span = self - .source_map - .expressions - .get(&(expression as *const _ as usize)) - .cloned(); + self.source_span = + self.source_map.expressions.get(&(expression as *const _ as usize)).cloned(); self.mark_span(SemanticSpanKind::Identifier, name); let structure = match canonical_expression_type(self.program, &object_type) { Some(WaveType::Struct(name)) => Some(name), @@ -1509,9 +1401,7 @@ impl<'a> Validator<'a> { }, _ => None, }; - let method = structure - .as_ref() - .and_then(|owner| self.program.method_type(owner, name)); + let method = structure.as_ref().and_then(|owner| self.program.method_type(owner, name)); let inherent = method.is_some(); let Some(mut signature) = method.or_else(|| self.program.functions.get(name).cloned()) else { @@ -1577,9 +1467,8 @@ impl<'a> Validator<'a> { let (function, mut all_args) = if inherent { let owner = structure.as_ref().unwrap(); let base = self.program.named_type_base(owner); - let owner_args = parse_named_type_application(owner) - .map(|(_, args)| args) - .unwrap_or_default(); + let owner_args = + parse_named_type_application(owner).map(|(_, args)| args).unwrap_or_default(); (crate::methods::method_symbol(base, name), owner_args) } else { (name.to_string(), Vec::new()) @@ -1590,11 +1479,7 @@ impl<'a> Validator<'a> { let Some(receiver) = signature.params.first() else { return Err(format!("method `{name}` requires a receiver parameter")); }; - self.require_assignable( - &object_type, - receiver, - &format!("receiver of method `{name}`"), - )?; + self.require_assignable(&object_type, receiver, &format!("receiver of method `{name}`"))?; self.validate_call_arguments( "method", name, @@ -1620,12 +1505,7 @@ impl<'a> Validator<'a> { .collect(); self.generic_method_calls.insert( expression as *const _ as usize, - crate::methods::GenericMethodCall { - function, - type_args, - defaults, - rewrite, - }, + crate::methods::GenericMethodCall { function, type_args, defaults, rewrite }, ); Ok(ExpressionType::Known(signature.return_type)) } @@ -1683,7 +1563,7 @@ impl<'a> Validator<'a> { .and_then(i128::checked_neg) .ok_or_else(|| format!("integer literal `{}` overflows", raw))?; Ok(ExpressionType::IntLiteral(value.to_string())) - } + }, other => Ok(other), } } else { @@ -1891,10 +1771,10 @@ impl ConditionMutation { match self { Self::Assignment(_) => { "use `==` for comparison, or move the assignment before the condition" - } + }, Self::CompoundAssignment(_) | Self::IncrementOrDecrement(_) => { "move the mutation before the condition" - } + }, } } } @@ -1910,20 +1790,20 @@ fn condition_mutation(expression: &Expression) -> Option { } else { Some(ConditionMutation::CompoundAssignment(symbol)) } - } + }, Expression::IncDec { kind, .. } => { Some(ConditionMutation::IncrementOrDecrement(match kind { IncDecKind::PreInc | IncDecKind::PostInc => "++", IncDecKind::PreDec | IncDecKind::PostDec => "--", })) - } - Expression::StructLiteral { fields, .. } => fields - .iter() - .find_map(|(_, value)| condition_mutation(value)), + }, + Expression::StructLiteral { fields, .. } => { + fields.iter().find_map(|(_, value)| condition_mutation(value)) + }, Expression::FunctionCall { args, .. } => args.iter().find_map(condition_mutation), Expression::MethodCall { object, args, .. } => { condition_mutation(object).or_else(|| args.iter().find_map(condition_mutation)) - } + }, Expression::Deref(inner) | Expression::AddressOf(inner) | Expression::Await(inner) @@ -1932,17 +1812,13 @@ fn condition_mutation(expression: &Expression) -> Option { | Expression::Cast { expr: inner, .. } | Expression::FieldAccess { object: inner, .. } => condition_mutation(inner), Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } => condition_mutation(left).or_else(|| condition_mutation(right)), + | Expression::IndexAccess { target: left, index: right } => { + condition_mutation(left).or_else(|| condition_mutation(right)) + }, Expression::ArrayLiteral(values) => values.iter().find_map(condition_mutation), - Expression::AsmBlock { - inputs, outputs, .. - } => inputs - .iter() - .chain(outputs.iter()) - .find_map(|(_, value)| condition_mutation(value)), + Expression::AsmBlock { inputs, outputs, .. } => { + inputs.iter().chain(outputs.iter()).find_map(|(_, value)| condition_mutation(value)) + }, Expression::Null | Expression::Literal(_) | Expression::Variable(_) => None, } } @@ -1962,25 +1838,16 @@ fn node_breaks_current_loop(node: &ASTNode) -> bool { match statement { StatementNode::Break => true, - StatementNode::If { - body, - else_if_blocks, - else_block, - .. - } => { + StatementNode::If { body, else_if_blocks, else_block, .. } => { block_breaks_current_loop(body) || else_if_blocks.as_ref().is_some_and(|blocks| { - blocks - .iter() - .any(|(_, block)| block_breaks_current_loop(block)) + blocks.iter().any(|(_, block)| block_breaks_current_loop(block)) }) - || else_block - .as_ref() - .is_some_and(|block| block_breaks_current_loop(block)) - } + || else_block.as_ref().is_some_and(|block| block_breaks_current_loop(block)) + }, StatementNode::Match { arms, .. } => { arms.iter().any(|arm| block_breaks_current_loop(&arm.body)) - } + }, StatementNode::While { .. } | StatementNode::For { .. } => false, _ => false, } @@ -1992,19 +1859,11 @@ fn variant_pattern_key(pattern: &MatchPattern) -> String { MatchPattern::Int(raw) => format!("int:{}", raw), MatchPattern::Ident(name) => format!("ident:{}", name), MatchPattern::Binding(_) | MatchPattern::Wildcard => "*".to_string(), - MatchPattern::Variant { - variant_type, - case_name, - payloads, - } => format!( + MatchPattern::Variant { variant_type, case_name, payloads } => format!( "{}::{}({})", variant_type, case_name, - payloads - .iter() - .map(variant_pattern_key) - .collect::>() - .join(",") + payloads.iter().map(variant_pattern_key).collect::>().join(",") ), } } @@ -2104,7 +1963,7 @@ fn analyze_program_types( } else { validator.validate_function(function, &function.name, &[]) } - } + }, ASTNode::ProtoImpl(implementation) => { let mut result = Ok(()); for method in &implementation.methods { @@ -2119,7 +1978,7 @@ fn analyze_program_types( } } result - } + }, ASTNode::Struct(structure) => { let mut result = Ok(()); for method in &structure.methods { @@ -2134,7 +1993,7 @@ fn analyze_program_types( } } result - } + }, ASTNode::ExternFunction(function) => { if !is_supported_foreign_abi(&function.abi) { validator.mark_span(SemanticSpanKind::Keyword, "extern"); @@ -2145,10 +2004,10 @@ fn analyze_program_types( } else { Ok(()) } - } + }, ASTNode::Variable(_) | ASTNode::Statement(_) | ASTNode::Expression(_) => { validator.validate_node(node).map(|_| ()) - } + }, _ => Ok(()), }; if let Err(message) = result { @@ -2201,18 +2060,12 @@ fn validate_declaration_types( .map(|p| &p.param_type) .chain(f.return_type.iter()) .collect::>(), - ASTNode::ExternFunction(f) => f - .params - .iter() - .map(|(_, t)| t) - .chain(std::iter::once(&f.return_type)) - .collect(), + ASTNode::ExternFunction(f) => { + f.params.iter().map(|(_, t)| t).chain(std::iter::once(&f.return_type)).collect() + }, _ => Vec::new(), }; - if ffi_types - .iter() - .any(|t| contains_future(program, t, &mut HashSet::new())) - { + if ffi_types.iter().any(|t| contains_future(program, t, &mut HashSet::new())) { return Err(semantic_diagnostic_for_top_level( nodes, index, @@ -2265,7 +2118,7 @@ fn validate_declaration_types( } } result - } + }, ASTNode::Struct(structure) => { let mut result = validate_unique_generic_params(&structure.generic_params, &structure.name); @@ -2285,7 +2138,7 @@ fn validate_declaration_types( } } result - } + }, ASTNode::ProtoImpl(implementation) => { if !program.is_known_named_type(&implementation.target) { Err(format!( @@ -2295,7 +2148,7 @@ fn validate_declaration_types( } else { Ok(()) } - } + }, ASTNode::TypeAlias(alias) => program .validate_type( &alias.target, @@ -2329,16 +2182,14 @@ fn validate_declaration_types( } else { result } - } + }, ASTNode::Variant(variant) => { let mut result = validate_unique_generic_params(&variant.generic_params, &variant.name); let generics: HashSet = variant.generic_params.iter().cloned().collect(); if result.is_ok() && variant.cases.is_empty() { - result = Err(format!( - "variant `{}` must declare at least one case", - variant.name - )); + result = + Err(format!("variant `{}` must declare at least one case", variant.name)); } if result.is_ok() { for case in &variant.cases { @@ -2362,7 +2213,7 @@ fn validate_declaration_types( result = validate_finite_variant(&variant.name, program); } result - } + }, ASTNode::ExternFunction(function) => { let mut params = HashSet::new(); let mut result = Ok(()); @@ -2378,10 +2229,7 @@ fn validate_declaration_types( ty, &no_generics, false, - &format!( - "parameter `{}` of extern function `{}`", - name, function.name - ), + &format!("parameter `{}` of extern function `{}`", name, function.name), ); if result.is_err() { break; @@ -2413,7 +2261,7 @@ fn validate_declaration_types( )); } result - } + }, ASTNode::Variable(variable) => program.validate_type( &variable.type_name, &no_generics, @@ -2423,9 +2271,7 @@ fn validate_declaration_types( _ => Ok(()), }; if let Err(message) = result { - return Err(semantic_diagnostic_for_top_level( - nodes, index, message, None, - )); + return Err(semantic_diagnostic_for_top_level(nodes, index, message, None)); } } @@ -2490,10 +2336,10 @@ fn validate_finite_payload_type( WaveType::Pointer(_) => Ok(()), WaveType::Array(inner, _) => { validate_finite_payload_type(root, inner, program, active, checked) - } + }, WaveType::Struct(name) | WaveType::Variant(name) => { validate_finite_named_type(root, name, program, active, checked) - } + }, _ => Ok(()), } } @@ -2527,10 +2373,10 @@ fn validate_alias_type_cycle( match ty { WaveType::Struct(name) if program.aliases.contains_key(name) => { validate_alias_cycle(name, program, active, checked) - } + }, WaveType::Future(inner) | WaveType::Pointer(inner) | WaveType::Array(inner, _) => { validate_alias_type_cycle(inner, program, active, checked) - } + }, _ => Ok(()), } } @@ -2539,10 +2385,7 @@ fn validate_unique_generic_params(params: &[String], owner: &str) -> Result<(), let mut seen = HashSet::new(); for param in params { if !seen.insert(param) { - return Err(format!( - "duplicate generic parameter `{}` in `{}`", - param, owner - )); + return Err(format!("duplicate generic parameter `{}` in `{}`", param, owner)); } } Ok(()) @@ -2553,7 +2396,7 @@ fn contains_future(program: &ProgramTypes, ty: &WaveType, seen: &mut HashSet true, WaveType::Pointer(inner) | WaveType::Array(inner, _) => { contains_future(program, inner, seen) - } + }, WaveType::Struct(name) | WaveType::Variant(name) if seen.insert(name.clone()) => { if let Some(alias) = program.aliases.get(name) { if contains_future(program, alias, seen) { @@ -2566,16 +2409,12 @@ fn contains_future(program: &ProgramTypes, ty: &WaveType, seen: &mut HashSet false, } } diff --git a/front/parser/src/verification/const_dependencies.rs b/front/parser/src/verification/const_dependencies.rs index 3e78d0aa..96b01100 100644 --- a/front/parser/src/verification/const_dependencies.rs +++ b/front/parser/src/verification/const_dependencies.rs @@ -13,7 +13,7 @@ pub(super) fn validate( .filter_map(|(index, node)| match node { ASTNode::Variable(variable) if variable.mutability == Mutability::Const => { Some((index, variable)) - } + }, _ => None, }) .collect(); @@ -30,10 +30,7 @@ pub(super) fn validate( if let Some(&dependency) = names.get(name.as_str()) { edges[index].push(( dependency, - sources - .expressions - .get(&(expression as *const _ as usize)) - .cloned(), + sources.expressions.get(&(expression as *const _ as usize)).cloned(), )); } } @@ -61,12 +58,9 @@ pub(super) fn validate( 0 => { colors[*dependency] = 1; stack.push((*dependency, 0)); - } + }, 1 => { - let start = stack - .iter() - .position(|(node, _)| node == dependency) - .unwrap(); + let start = stack.iter().position(|(node, _)| node == dependency).unwrap(); let cycle: Vec<_> = stack[start..] .iter() .map(|(node, _)| *node) @@ -114,8 +108,8 @@ pub(super) fn validate( ); diagnostic.help = "break the constant dependency cycle; noncyclic forward references are allowed".into(); return Err(diagnostic); - } - _ => {} + }, + _ => {}, } } } diff --git a/front/parser/src/verification/diagnostics.rs b/front/parser/src/verification/diagnostics.rs index b2e7a626..be0bc295 100644 --- a/front/parser/src/verification/diagnostics.rs +++ b/front/parser/src/verification/diagnostics.rs @@ -56,18 +56,14 @@ pub(super) fn top_level_span_hint(node: &ASTNode) -> SemanticSpanHint { ASTNode::Struct(structure) => (SemanticSpanKind::Declaration, structure.name.clone()), ASTNode::ProtoImpl(implementation) => { (SemanticSpanKind::Declaration, implementation.target.clone()) - } + }, ASTNode::TypeAlias(alias) => (SemanticSpanKind::Declaration, alias.name.clone()), ASTNode::Enum(enumeration) => (SemanticSpanKind::Declaration, enumeration.name.clone()), ASTNode::Variant(variant) => (SemanticSpanKind::Declaration, variant.name.clone()), ASTNode::Variable(variable) => (SemanticSpanKind::Declaration, variable.name.clone()), ASTNode::Statement(_) | ASTNode::Expression(_) | ASTNode::Program(_) => { (SemanticSpanKind::Keyword, "program".to_string()) - } + }, }; - SemanticSpanHint { - kind, - text, - occurrence: 1, - } + SemanticSpanHint { kind, text, occurrence: 1 } } diff --git a/front/parser/src/verification/expression.rs b/front/parser/src/verification/expression.rs index 0ade3034..323fe412 100644 --- a/front/parser/src/verification/expression.rs +++ b/front/parser/src/verification/expression.rs @@ -20,35 +20,34 @@ impl Validator<'_> { Expression::Deref(..) => self.validate_expr_deref(expression, expected), Expression::BinaryExpression { .. } => { self.validate_expr_binary_expression(expression, expected) - } + }, Expression::Await(..) => self.validate_expr_await(expression, expected), Expression::Unary { .. } => self.validate_expr_unary(expression, expected), Expression::FunctionCall { .. } => { self.validate_expr_function_call(expression, expected) - } + }, Expression::MethodCall { .. } => self.validate_expr_method_call(expression, expected), Expression::StructLiteral { .. } => { self.validate_expr_struct_literal(expression, expected) - } + }, Expression::FieldAccess { .. } => self.validate_expr_field_access(expression, expected), Expression::IndexAccess { .. } => self.validate_expr_index_access(expression, expected), Expression::ArrayLiteral(..) => self.validate_expr_array_literal(expression, expected), Expression::Assignment { .. } => self.validate_expr_assignment(expression, expected), Expression::AssignOperation { .. } => { self.validate_expr_assign_operation(expression, expected) - } + }, Expression::IncDec { .. } => self.validate_expr_inc_dec(expression, expected), Expression::AsmBlock { .. } => self.validate_expr_asm_block(expression, expected), } } + fn validate_expr_literal( &mut self, expression: &Expression, expected: Option<&WaveType>, ) -> Result { - let Expression::Literal(literal) = expression else { - unreachable!("expression dispatch") - }; + let Expression::Literal(literal) = expression else { unreachable!("expression dispatch") }; Ok(match literal { Literal::Int(raw) => { if let Some(WaveType::Float(bits)) = @@ -67,7 +66,7 @@ impl Validator<'_> { } } ExpressionType::IntLiteral(raw.clone()) - } + }, Literal::Float(value) => { if !value.is_finite() || (matches!( @@ -76,11 +75,11 @@ impl Validator<'_> { ) && !(*value as f32).is_finite()) { return Err( - "floating-point literal is out of range for its expected type".to_string(), + "floating-point literal is out of range for its expected type".to_string() ); } ExpressionType::FloatLiteral - } + }, Literal::String(_) => ExpressionType::Known(WaveType::String), Literal::Bool(_) => ExpressionType::Known(WaveType::Bool), Literal::Char(_) => ExpressionType::Known(WaveType::Char), @@ -93,9 +92,7 @@ impl Validator<'_> { expression: &Expression, expected: Option<&WaveType>, ) -> Result { - let Expression::Variable(name) = expression else { - unreachable!("expression dispatch") - }; + let Expression::Variable(name) = expression else { unreachable!("expression dispatch") }; if self.program.variant_constructor(name).is_some() { return self.validate_variant_constructor(expression, name, &[], expected); @@ -118,8 +115,7 @@ impl Validator<'_> { }; self.mark_span(SemanticSpanKind::Keyword, "as"); - self.program - .validate_type(target_type, &self.current_type_params, false, "cast target")?; + self.program.validate_type(target_type, &self.current_type_params, false, "cast target")?; let source = self.validate_expr_expected(expr, Some(target_type))?; if !self.is_valid_cast(&source, target_type) { return Err(format!( @@ -136,9 +132,7 @@ impl Validator<'_> { expression: &Expression, expected: Option<&WaveType>, ) -> Result { - let Expression::AddressOf(inner) = expression else { - unreachable!("expression dispatch") - }; + let Expression::AddressOf(inner) = expression else { unreachable!("expression dispatch") }; self.mark_span(SemanticSpanKind::Keyword, "&"); if !is_lvalue_expression(inner) && !matches!(inner.as_ref(), Expression::ArrayLiteral(_)) { @@ -149,15 +143,14 @@ impl Validator<'_> { } // Addressed literals borrow their complete array layout from // the validated pointer destination, including element widths. - let array_context = expected - .map(|ty| self.program.canonical_type(ty)) - .and_then(|ty| match ty { + let array_context = + expected.map(|ty| self.program.canonical_type(ty)).and_then(|ty| match ty { WaveType::Pointer(pointee) if matches!(inner.as_ref(), Expression::ArrayLiteral(_)) && matches!(pointee.as_ref(), WaveType::Array(_, _)) => { Some(*pointee) - } + }, _ => None, }); let inner_type = self.validate_expr_expected(inner, array_context.as_ref())?; @@ -169,7 +162,7 @@ impl Validator<'_> { ExpressionType::Known(ty) => ExpressionType::Known(WaveType::Pointer(Box::new(ty))), ExpressionType::ArrayLiteral(elements) => { ExpressionType::AddressedArrayLiteral(elements) - } + }, _ => ExpressionType::Unknown, }) } @@ -179,9 +172,7 @@ impl Validator<'_> { expression: &Expression, _expected: Option<&WaveType>, ) -> Result { - let Expression::Deref(inner) = expression else { - unreachable!("expression dispatch") - }; + let Expression::Deref(inner) = expression else { unreachable!("expression dispatch") }; self.mark_span(SemanticSpanKind::Keyword, "deref"); let mut projection = inner.as_ref(); @@ -201,10 +192,9 @@ impl Validator<'_> { }; match inner_type { ExpressionType::Known(WaveType::Pointer(ty)) => Ok(ExpressionType::Known(*ty)), - other => Err(format!( - "deref expects a pointer, found `{}`", - display_expression_type(&other) - )), + other => { + Err(format!("deref expects a pointer, found `{}`", display_expression_type(&other))) + }, } } @@ -213,12 +203,7 @@ impl Validator<'_> { expression: &Expression, expected: Option<&WaveType>, ) -> Result { - let Expression::BinaryExpression { - left, - operator, - right, - } = expression - else { + let Expression::BinaryExpression { left, operator, right } = expression else { unreachable!("expression dispatch") }; @@ -246,17 +231,17 @@ impl Validator<'_> { self.validate_integer_operand(left, &operand_type)?, self.validate_integer_operand(right, &operand_type)?, ) - } + }, (true, false) => { let right_type = self.validate_expr(right)?; let left_type = self.validate_contextual_operand(left, &right_type)?; (left_type, right_type) - } + }, (false, true) => { let left_type = self.validate_expr(left)?; let right_type = self.validate_contextual_operand(right, &left_type)?; (left_type, right_type) - } + }, (false, false) => (self.validate_expr(left)?, self.validate_expr(right)?), }; infer_binary_type(self.program, operator, left_type, right_type) @@ -267,9 +252,7 @@ impl Validator<'_> { expression: &Expression, _expected: Option<&WaveType>, ) -> Result { - let Expression::Await(inner) = expression else { - unreachable!("expression dispatch") - }; + let Expression::Await(inner) = expression else { unreachable!("expression dispatch") }; if !self.current_async { return Err("await is only valid inside an async function".into()); @@ -302,12 +285,7 @@ impl Validator<'_> { expression: &Expression, expected: Option<&WaveType>, ) -> Result { - let Expression::FunctionCall { - name, - type_args, - args, - } = expression - else { + let Expression::FunctionCall { name, type_args, args } = expression else { unreachable!("expression dispatch") }; @@ -330,13 +308,7 @@ impl Validator<'_> { expression: &Expression, _expected: Option<&WaveType>, ) -> Result { - let Expression::MethodCall { - object, - name, - args, - type_args, - } = expression - else { + let Expression::MethodCall { object, name, args, type_args } = expression else { unreachable!("expression dispatch") }; self.validate_method_call(expression, object, name, type_args, args) @@ -408,11 +380,7 @@ impl Validator<'_> { ExpressionType::Known(ty) => ExpressionType::Known(self.program.canonical_type(&ty)), other => other, }; - if let Some(span) = self - .source_map - .expressions - .get(&(expression as *const _ as usize)) - { + if let Some(span) = self.source_map.expressions.get(&(expression as *const _ as usize)) { self.source_span = Some(span.clone()); } let structure = match &object_type { @@ -463,7 +431,7 @@ impl Validator<'_> { ExpressionType::Known(WaveType::String) => Ok(ExpressionType::Known(WaveType::Int(8))), ExpressionType::Known(WaveType::Array(element, _)) => { Ok(ExpressionType::Known(*element)) - } + }, ExpressionType::Known(WaveType::Pointer(element)) => match *element { WaveType::Array(array_element, _) => Ok(ExpressionType::Known(*array_element)), other => Ok(ExpressionType::Known(other)), @@ -485,9 +453,8 @@ impl Validator<'_> { }; self.mark_span(SemanticSpanKind::Keyword, "["); - let expected_element = expected - .map(|ty| self.program.canonical_type(ty)) - .and_then(|ty| match ty { + let expected_element = + expected.map(|ty| self.program.canonical_type(ty)).and_then(|ty| match ty { WaveType::Array(element, _) => Some(*element), _ => None, }); @@ -536,19 +503,11 @@ impl Validator<'_> { expression: &Expression, _expected: Option<&WaveType>, ) -> Result { - let Expression::AssignOperation { - target, - operator, - value, - } = expression - else { + let Expression::AssignOperation { target, operator, value } = expression else { unreachable!("expression dispatch") }; - self.mark_span( - SemanticSpanKind::Keyword, - assign_operator_source_symbol(operator), - ); + self.mark_span(SemanticSpanKind::Keyword, assign_operator_source_symbol(operator)); if !is_lvalue_expression(target) { return Err("compound assignment target is not an lvalue".to_string()); } @@ -590,11 +549,7 @@ impl Validator<'_> { )); } if let ExpressionType::Known(expected) = &target_type { - self.require_assignable( - &value_type, - expected, - "right operand of compound assignment", - )?; + self.require_assignable(&value_type, expected, "right operand of compound assignment")?; } Ok(target_type) } @@ -640,10 +595,7 @@ impl Validator<'_> { expression: &Expression, _expected: Option<&WaveType>, ) -> Result { - let Expression::AsmBlock { - inputs, outputs, .. - } = expression - else { + let Expression::AsmBlock { inputs, outputs, .. } = expression else { unreachable!("expression dispatch") }; diff --git a/front/parser/src/verification/numeric.rs b/front/parser/src/verification/numeric.rs index 414960d2..2b3f22a0 100644 --- a/front/parser/src/verification/numeric.rs +++ b/front/parser/src/verification/numeric.rs @@ -91,21 +91,16 @@ pub(super) fn infer_binary_type( let left_pointer = left_canonical.as_ref().is_some_and(is_pointer_like_type); let right_pointer = right_canonical.as_ref().is_some_and(is_pointer_like_type); - let left_integer = left_canonical - .as_ref() - .is_some_and(|ty| integer_bit_width(ty).is_some()); - let right_integer = right_canonical - .as_ref() - .is_some_and(|ty| integer_bit_width(ty).is_some()); + let left_integer = left_canonical.as_ref().is_some_and(|ty| integer_bit_width(ty).is_some()); + let right_integer = right_canonical.as_ref().is_some_and(|ty| integer_bit_width(ty).is_some()); let left_numeric = left_canonical.as_ref().is_some_and(is_numeric_type); let right_numeric = right_canonical.as_ref().is_some_and(is_numeric_type); if let (ExpressionType::Known(left_known), ExpressionType::Known(right_known)) = (&left, &right) { - if let (WaveType::Float(left_bits), WaveType::Float(right_bits)) = ( - program.canonical_type(left_known), - program.canonical_type(right_known), - ) { + if let (WaveType::Float(left_bits), WaveType::Float(right_bits)) = + (program.canonical_type(left_known), program.canonical_type(right_known)) + { if left_bits != right_bits { return Err(format!( "mixed float widths require an explicit cast: found `f{}` and `f{}`", @@ -124,20 +119,16 @@ pub(super) fn infer_binary_type( if left_pointer || right_pointer { let valid = match (left_pointer, right_pointer) { - (true, true) => matches!( - operator, - Operator::Equal | Operator::NotEqual | Operator::Subtract - ), + (true, true) => { + matches!(operator, Operator::Equal | Operator::NotEqual | Operator::Subtract) + }, (true, false) if right_integer => matches!( operator, Operator::Add | Operator::Subtract | Operator::Equal | Operator::NotEqual ), (false, true) if left_integer => { - matches!( - operator, - Operator::Add | Operator::Equal | Operator::NotEqual - ) - } + matches!(operator, Operator::Add | Operator::Equal | Operator::NotEqual) + }, _ => false, }; if !valid { @@ -255,12 +246,12 @@ pub(super) fn contextual_integer_expression( if integer_bit_width(&program.canonical_type(ty)).is_some() => { right - } + }, (ExpressionType::Known(ty), ExpressionType::IntLiteral(_)) if integer_bit_width(&program.canonical_type(ty)).is_some() => { left - } + }, _ => wider_integer_expression(program, left, right), } } @@ -304,7 +295,7 @@ pub(super) fn integer_literal_fits(raw: &str, ty: &WaveType) -> bool { // Non-decimal literals may spell the full-width bit pattern. bit_len <= bits } - } + }, WaveType::Uint(bits) if *bits > 0 => !negative && bit_len <= usize::from(*bits), WaveType::Char | WaveType::Byte => !negative && bit_len <= 8, _ => false, @@ -313,11 +304,7 @@ pub(super) fn integer_literal_fits(raw: &str, ty: &WaveType) -> bool { pub(super) fn integer_literal_parts(raw: &str) -> Option<(bool, u32, String)> { let n = lexer::number::IntegerLiteral::parse(raw)?; - Some(( - n.negative, - n.radix, - n.digits.trim_start_matches('0').to_string(), - )) + Some((n.negative, n.radix, n.digits.trim_start_matches('0').to_string())) } pub(super) fn unsigned_literal_bit_len(radix: u32, digits: &str) -> Option { @@ -331,7 +318,7 @@ pub(super) fn unsigned_literal_bit_len(radix: u32, digits: &str) -> Option { let mut decimal: Vec = digits.bytes().map(|byte| byte - b'0').collect(); let mut bits = 0usize; @@ -345,7 +332,7 @@ pub(super) fn unsigned_literal_bit_len(radix: u32, digits: &str) -> Option None, } } @@ -394,10 +381,7 @@ pub(super) fn unsigned_is_power_of_two(radix: u32, digits: &str) -> bool { } pub(super) fn is_integer_type(ty: &WaveType) -> bool { - matches!( - ty, - WaveType::Int(_) | WaveType::Uint(_) | WaveType::Char | WaveType::Byte - ) + matches!(ty, WaveType::Int(_) | WaveType::Uint(_) | WaveType::Char | WaveType::Byte) } pub(super) fn integer_bit_width(ty: &WaveType) -> Option { @@ -418,10 +402,7 @@ pub(super) fn is_pointer_like_type(ty: &WaveType) -> bool { } pub(super) fn is_byte_like_type(ty: &WaveType) -> bool { - matches!( - ty, - WaveType::Int(8) | WaveType::Uint(8) | WaveType::Char | WaveType::Byte - ) + matches!(ty, WaveType::Int(8) | WaveType::Uint(8) | WaveType::Char | WaveType::Byte) } pub(super) fn require_assignable( @@ -521,7 +502,7 @@ pub(super) fn is_assignable( ExpressionType::IntLiteral(raw) => { integer_literal_fits(raw, &expected) || (matches!(expected, WaveType::Pointer(_)) && int_literal_is_zero(raw)) - } + }, ExpressionType::FloatLiteral => matches!(expected, WaveType::Float(_)), ExpressionType::Known(actual) => { if program.is_generic_placeholder(actual) { @@ -538,16 +519,16 @@ pub(super) fn is_assignable( && integer_bit_width(expected).is_some() => { integer_bit_width(actual) <= integer_bit_width(expected) - } + }, (WaveType::Int(_) | WaveType::Uint(_), WaveType::Float(_)) | (WaveType::Float(_), WaveType::Int(_) | WaveType::Uint(_)) => true, (WaveType::String, WaveType::Pointer(inner)) => { is_byte_like_type(inner.as_ref()) || matches!(inner.as_ref(), WaveType::String) - } + }, (WaveType::Pointer(actual), WaveType::Pointer(expected)) => actual == expected, _ => false, } - } + }, } } @@ -570,10 +551,8 @@ pub(super) fn is_valid_cast( if matches!(source, ExpressionType::Null) { return matches!(target, WaveType::Pointer(_)); } - if matches!( - source, - ExpressionType::ArrayLiteral(_) | ExpressionType::AddressedArrayLiteral(_) - ) { + if matches!(source, ExpressionType::ArrayLiteral(_) | ExpressionType::AddressedArrayLiteral(_)) + { return false; } @@ -618,11 +597,7 @@ mod tests { ("-128", WaveType::Int(8), true), ("-129", WaveType::Int(8), false), ("-1", WaveType::Uint(8), false), - ( - "340282366920938463463374607431768211455", - WaveType::Uint(128), - true, - ), + ("340282366920938463463374607431768211455", WaveType::Uint(128), true), ] { assert_eq!(integer_literal_fits(raw, &ty), fits, "{raw} {ty:?}"); } diff --git a/front/parser/src/verification/program.rs b/front/parser/src/verification/program.rs index 822f07df..1f89c870 100644 --- a/front/parser/src/verification/program.rs +++ b/front/parser/src/verification/program.rs @@ -60,28 +60,23 @@ impl ProgramTypes { } match node { ASTNode::Function(function) => { - out.generic_type_params - .extend(function.generic_params.iter().cloned()); - } + out.generic_type_params.extend(function.generic_params.iter().cloned()); + }, ASTNode::Struct(structure) => { - out.generic_type_params - .extend(structure.generic_params.iter().cloned()); + out.generic_type_params.extend(structure.generic_params.iter().cloned()); for method in &structure.methods { - out.generic_type_params - .extend(method.generic_params.iter().cloned()); + out.generic_type_params.extend(method.generic_params.iter().cloned()); } - } + }, ASTNode::Variant(variant) => { - out.generic_type_params - .extend(variant.generic_params.iter().cloned()); - } + out.generic_type_params.extend(variant.generic_params.iter().cloned()); + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { - out.generic_type_params - .extend(method.generic_params.iter().cloned()); + out.generic_type_params.extend(method.generic_params.iter().cloned()); } - } - _ => {} + }, + _ => {}, } } @@ -102,7 +97,7 @@ impl ProgramTypes { function_type(function), ) .map_err(|message| failure(message, Some(top_level_span_hint(node))))?; - } + }, ASTNode::ExternFunction(function) => { insert_unique_value_name(&mut value_names, &function.name) .map_err(|message| failure(message, Some(top_level_span_hint(node))))?; @@ -119,7 +114,7 @@ impl ProgramTypes { }, ) .map_err(|message| failure(message, Some(top_level_span_hint(node))))?; - } + }, ASTNode::Struct(structure) => { let mut fields = HashMap::new(); for (name, ty) in &structure.fields { @@ -158,7 +153,7 @@ impl ProgramTypes { ) })?; } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { let signature = function_type(method); @@ -185,10 +180,10 @@ impl ProgramTypes { insert_unique_function(&mut out.functions, &lowered, signature) .map_err(|message| failure(message, Some(top_level_span_hint(node))))?; } - } + }, ASTNode::TypeAlias(alias) => { out.aliases.insert(alias.name.clone(), alias.target.clone()); - } + }, ASTNode::Variable(variable) if matches!(variable.mutability, Mutability::Const | Mutability::Static) => { @@ -196,25 +191,20 @@ impl ProgramTypes { .map_err(|message| failure(message, Some(top_level_span_hint(node))))?; out.globals.insert( variable.name.clone(), - Binding { - mutability: variable.mutability, - ty: variable.type_name.clone(), - }, + Binding { mutability: variable.mutability, ty: variable.type_name.clone() }, ); if matches!(variable.mutability, Mutability::Const) { if let Some(Expression::Literal(Literal::Int(raw))) = &variable.initial_value { if lexer::number::IntegerLiteral::parse(raw).is_some() { - out.constant_values - .insert(variable.name.clone(), raw.clone()); + out.constant_values.insert(variable.name.clone(), raw.clone()); } } } - } + }, ASTNode::Enum(enumeration) => { - out.enum_reprs - .insert(enumeration.name.clone(), enumeration.repr_type.clone()); + out.enum_reprs.insert(enumeration.name.clone(), enumeration.repr_type.clone()); let mut variants = HashSet::new(); let mut next = 0i128; for variant in &enumeration.variants { @@ -255,8 +245,7 @@ impl ProgramTypes { ) })?; } - out.constant_values - .insert(variant.name.clone(), next.to_string()); + out.constant_values.insert(variant.name.clone(), next.to_string()); next = next.checked_add(1).ok_or_else(|| { failure( format!("enum `{}` value overflow", enumeration.name), @@ -264,7 +253,7 @@ impl ProgramTypes { ) })?; } - } + }, ASTNode::Variant(variant) => { let mut names = HashSet::new(); let mut cases = Vec::with_capacity(variant.cases.len()); @@ -288,13 +277,10 @@ impl ProgramTypes { .insert(variant.name.clone(), variant.generic_params.clone()); out.variants.insert( variant.name.clone(), - VariantType { - generic_params: variant.generic_params.clone(), - cases, - }, + VariantType { generic_params: variant.generic_params.clone(), cases }, ); - } - _ => {} + }, + _ => {}, } } @@ -303,9 +289,7 @@ impl ProgramTypes { pub(super) fn is_known_named_type(&self, name: &str) -> bool { self.type_names.contains(name) - || name - .split_once('<') - .is_some_and(|(base, _)| self.type_names.contains(base.trim())) + || name.split_once('<').is_some_and(|(base, _)| self.type_names.contains(base.trim())) } pub(super) fn named_type_base<'a>(&self, name: &'a str) -> &'a str { @@ -313,9 +297,7 @@ impl ProgramTypes { } pub(super) fn struct_fields(&self, name: &str) -> Option<&HashMap> { - self.structs - .get(name) - .or_else(|| self.structs.get(self.named_type_base(name))) + self.structs.get(name).or_else(|| self.structs.get(self.named_type_base(name))) } pub(super) fn generic_substitution(&self, name: &str) -> HashMap { @@ -338,17 +320,10 @@ impl ProgramTypes { } pub(super) fn method_type(&self, owner: &str, name: &str) -> Option { - let signature = self - .methods - .get(&(owner.to_string(), name.to_string())) - .or_else(|| { - self.methods - .get(&(self.named_type_base(owner).to_string(), name.to_string())) - })?; - Some(substitute_function_type( - signature, - &self.generic_substitution(owner), - )) + let signature = self.methods.get(&(owner.to_string(), name.to_string())).or_else(|| { + self.methods.get(&(self.named_type_base(owner).to_string(), name.to_string())) + })?; + Some(substitute_function_type(signature, &self.generic_substitution(owner))) } pub(super) fn is_generic_placeholder(&self, ty: &WaveType) -> bool { @@ -356,9 +331,7 @@ impl ProgramTypes { } pub(super) fn variant_type(&self, name: &str) -> Option<&VariantType> { - self.variants - .get(name) - .or_else(|| self.variants.get(self.named_type_base(name))) + self.variants.get(name).or_else(|| self.variants.get(self.named_type_base(name))) } pub(super) fn variant_constructor<'b>(&self, name: &'b str) -> Option<(&'b str, &'b str)> { @@ -370,12 +343,8 @@ impl ProgramTypes { pub(super) fn variant_case(&self, owner: &str, case: &str) -> Option<(u32, Vec)> { let definition = self.variant_type(owner)?; let substitution = self.generic_substitution(owner); - definition - .cases - .iter() - .enumerate() - .find(|(_, (name, _))| name == case) - .map(|(index, (_, payloads))| { + definition.cases.iter().enumerate().find(|(_, (name, _))| name == case).map( + |(index, (_, payloads))| { ( index as u32, payloads @@ -383,7 +352,8 @@ impl ProgramTypes { .map(|ty| self.canonical_type(&substitute_wave_type(ty, &substitution))) .collect(), ) - }) + }, + ) } pub(super) fn validate_type( @@ -474,16 +444,16 @@ impl ProgramTypes { }; seen.remove(name); resolved - } + }, WaveType::Future(inner) => { WaveType::Future(Box::new(self.canonical_type_inner(inner, seen))) - } + }, WaveType::Pointer(inner) => { WaveType::Pointer(Box::new(self.canonical_type_inner(inner, seen))) - } + }, WaveType::Array(inner, size) => { WaveType::Array(Box::new(self.canonical_type_inner(inner, seen)), *size) - } + }, WaveType::Variant(name) => WaveType::Variant(name.clone()), _ => ty.clone(), } @@ -516,10 +486,7 @@ fn insert_unique_method( name: &str, signature: FunctionType, ) -> Result<(), String> { - if methods - .insert((owner.to_string(), name.to_string()), signature) - .is_none() - { + if methods.insert((owner.to_string(), name.to_string()), signature).is_none() { Ok(()) } else { Err(format!("duplicate method `{}.{}`", owner, name)) @@ -532,25 +499,15 @@ pub(super) fn parse_integer_value(raw: &str) -> Option { pub(super) fn function_type(function: &FunctionNode) -> FunctionType { FunctionType { - defaults: function - .parameters - .iter() - .map(|p| p.initial_value.clone()) - .collect(), - params: function - .parameters - .iter() - .map(|parameter| parameter.param_type.clone()) - .collect(), + defaults: function.parameters.iter().map(|p| p.initial_value.clone()).collect(), + params: function.parameters.iter().map(|parameter| parameter.param_type.clone()).collect(), required_params: function .parameters .iter() .filter(|parameter| parameter.initial_value.is_none()) .count(), return_type: if function.is_async { - WaveType::Future(Box::new( - function.return_type.clone().unwrap_or(WaveType::Void), - )) + WaveType::Future(Box::new(function.return_type.clone().unwrap_or(WaveType::Void))) } else { function.return_type.clone().unwrap_or(WaveType::Void) }, @@ -593,16 +550,16 @@ pub(super) fn substitute_wave_type( } else { ty.clone() } - } + }, WaveType::Future(inner) => { WaveType::Future(Box::new(substitute_wave_type(inner, substitutions))) - } + }, WaveType::Pointer(inner) => { WaveType::Pointer(Box::new(substitute_wave_type(inner, substitutions))) - } + }, WaveType::Array(inner, size) => { WaveType::Array(Box::new(substitute_wave_type(inner, substitutions)), *size) - } + }, WaveType::Variant(name) => { if let Some((base, arguments)) = parse_named_type_application(name) { let arguments = arguments @@ -616,7 +573,7 @@ pub(super) fn substitute_wave_type( } else { ty.clone() } - } + }, _ => ty.clone(), } } diff --git a/front/parser/tests/control_diagnostics.rs b/front/parser/tests/control_diagnostics.rs index 31e3e66c..69994aee 100644 --- a/front/parser/tests/control_diagnostics.rs +++ b/front/parser/tests/control_diagnostics.rs @@ -3,9 +3,7 @@ use lexer::Lexer; use parser::{parse_syntax_only, parse_syntax_with_spans, ParseError}; fn diagnostic(source: &str) -> ParseError { - let tokens = Lexer::new_with_file(source, "control.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(source, "control.wave").tokenize().unwrap(); parse_syntax_with_spans(&tokens).unwrap_err() } @@ -17,34 +15,18 @@ fn missing_header_delimiters_identify_the_unexpected_token() { ("if (true) @return;", "'{'", "if header"), ("if (true) {} else if @false) {}", "'('", "else if header"), ("if (true) {} else if (false @{ }", "')'", "else if header"), - ( - "if (true) {} else if (false) @return;", - "'{'", - "else if header", - ), + ("if (true) {} else if (false) @return;", "'{'", "else if header"), ("if (true) {} else @return;", "'{'", "else header"), ("while @true) {}", "'('", "while header"), ("while (true @{ }", "')'", "while header"), ("while (true) @return;", "'{'", "while header"), ("for @i = 0; i < 2; i = i + 1) {}", "'('", "for header"), ("for (i = 0 @i < 2; i = i + 1) {}", "';'", "for initializer"), - ( - "for (var i: i32 = 0 @i < 2; i = i + 1) {}", - "';'", - "for initializer", - ), - ( - "for (i: i32 = 0 @i < 2; i = i + 1) {}", - "';'", - "for initializer", - ), + ("for (var i: i32 = 0 @i < 2; i = i + 1) {}", "';'", "for initializer"), + ("for (i: i32 = 0 @i < 2; i = i + 1) {}", "';'", "for initializer"), ("for (i = 0; i < 2 @i = i + 1) {}", "';'", "for condition"), ("for (i = 0; i < 2; i = i + 1 @{ }", "')'", "for increment"), - ( - "for (i = 0; i < 2; i = i + 1) @return;", - "'{'", - "for header", - ), + ("for (i = 0; i < 2; i = i + 1) @return;", "'{'", "for header"), ]; for (body, expected, context) in cases { // Unicode and CRLF ensure byte offsets and source columns are not conflated. @@ -60,10 +42,7 @@ fn missing_header_delimiters_identify_the_unexpected_token() { error.column(), source[..start].rsplit('\n').next().unwrap().chars().count() + 1 ); - assert!(error - .found() - .unwrap() - .contains(&source[start..error.span().unwrap().end])); + assert!(error.found().unwrap().contains(&source[start..error.span().unwrap().end])); let tokens = Lexer::new(&source).tokenize().unwrap(); let without_spans = parse_syntax_only(&tokens).unwrap_err(); assert_eq!(without_spans.message(), error.message()); @@ -86,11 +65,7 @@ fn header_errors_survive_nested_blocks_exports_and_methods() { "proto S { fun f() { for (i = 0 i < 2; i = i + 1) {} } }", ] { let error = diagnostic(source); - assert_eq!( - error.context(), - Some("for initializer"), - "{source}: {error:?}" - ); + assert_eq!(error.context(), Some("for initializer"), "{source}: {error:?}"); assert_eq!(error.expected(), ["';'"]); assert_eq!(error.span().unwrap().start, source.find("i < 2").unwrap()); } diff --git a/front/parser/tests/declaration_asm_diagnostics.rs b/front/parser/tests/declaration_asm_diagnostics.rs index 8dc82b55..eb2148f7 100644 --- a/front/parser/tests/declaration_asm_diagnostics.rs +++ b/front/parser/tests/declaration_asm_diagnostics.rs @@ -5,9 +5,7 @@ fn failure(marked: &str, expected: &str, context: &str) { let marked = format!("// 한글\r\n{marked}"); let start = marked.find('@').unwrap(); let source = marked.replace('@', ""); - let tokens = Lexer::new_with_file(&source, "diagnostics.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "diagnostics.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert_eq!(error.span().unwrap().start, start, "{source}: {error:?}"); assert_eq!(error.expected(), [expected], "{source}: {error:?}"); @@ -27,11 +25,7 @@ fn declaration_errors_keep_the_failing_token_for_private_and_public_forms() { ("enum E -> @{}", "type", "enum representation type"), ("enum E -> i32 @X", "'{'", "enum declaration"), ("enum E -> i32 { @1 }", "identifier", "enum case"), - ( - "enum E -> i32 { A = @true }", - "integer literal", - "enum case value", - ), + ("enum E -> i32 { A = @true }", "integer literal", "enum case value"), ("enum E -> i32 { A @B }", "',' or '}'", "enum declaration"), ("variant @{ A }", "identifier", "variant declaration"), ("variant V @A", "'{'", "variant declaration"), @@ -53,42 +47,18 @@ fn declaration_errors_keep_the_failing_token_for_private_and_public_forms() { fn both_asm_forms_share_precise_clause_errors() { for (body, expected, context) in [ ("@;", "'{'", "asm block"), - ( - "{ @123 }", - "instruction string, in, out, clobber, or '}'", - "asm block", - ), + ("{ @123 }", "instruction string, in, out, clobber, or '}'", "asm block"), ("{ in @rax }", "'('", "asm input clause"), - ( - "{ in(@123) value }", - "register string or identifier", - "asm input clause", - ), + ("{ in(@123) value }", "register string or identifier", "asm input clause"), ("{ in(rax @value }", "')'", "asm input clause"), - ( - "{ out(rax) @123 }", - "assignable expression", - "asm output clause", - ), + ("{ out(rax) @123 }", "assignable expression", "asm output clause"), ("{ clobber @rax }", "'('", "asm clobber clause"), - ( - "{ clobber(@1) }", - "register string or identifier", - "asm clobber clause", - ), + ("{ clobber(@1) }", "register string or identifier", "asm clobber clause"), ("{ clobber(rax @rcx) }", "',' or ')'", "asm clobber clause"), - ( - "{ clobber(rax, @) }", - "register string or identifier", - "asm clobber clause", - ), + ("{ clobber(rax, @) }", "register string or identifier", "asm clobber clause"), ] { failure(&format!("fun f() {{ asm {body}; }}"), expected, context); - failure( - &format!("fun f() {{ var x: i32 = asm {body}; }}"), - expected, - context, - ); + failure(&format!("fun f() {{ var x: i32 = asm {body}; }}"), expected, context); } failure("fun f() { asm { @", "'}'", "asm block"); failure("fun f() { var x: i32 = asm { @", "'}'", "asm block"); diff --git a/front/parser/tests/expression_depth.rs b/front/parser/tests/expression_depth.rs index b431bb79..15a5da85 100644 --- a/front/parser/tests/expression_depth.rs +++ b/front/parser/tests/expression_depth.rs @@ -11,9 +11,7 @@ fn parser_depth_budget_is_restored_after_errors_and_counts_mixed_shapes() { format!("{}1", "x = ".repeat(129)), ] { let source = format!("fun main() {{ {expression}; }}"); - let tokens = Lexer::new_with_file(&source, "depth.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "depth.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert!(error.message().contains("maximum of 128"), "{error:?}"); assert_eq!(error.span().unwrap().file, "depth.wave"); @@ -35,9 +33,7 @@ fn struct_depth_boundaries_fit_a_small_native_stack() { "Item { value: ".repeat(depth), " }".repeat(depth), ); - let tokens = Lexer::new_with_file(&source, "struct-depth.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "struct-depth.wave").tokenize().unwrap(); for (with_spans, parse) in [ (false, parse_syntax_only as fn(&[lexer::Token]) -> _), (true, parse_syntax_with_spans), diff --git a/front/parser/tests/expression_diagnostics.rs b/front/parser/tests/expression_diagnostics.rs index 64b86bf6..892a30b4 100644 --- a/front/parser/tests/expression_diagnostics.rs +++ b/front/parser/tests/expression_diagnostics.rs @@ -5,31 +5,17 @@ use parser::{parse_syntax_with_spans, ParseError}; fn failure(marked: &str, expected: &str, context: &str) -> ParseError { let start = marked.find('@').expect("mark the offending token"); let source = marked.replace('@', ""); - let tokens = Lexer::new_with_file(&source, "expressions.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "expressions.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert_eq!(error.expected(), [expected], "{source}: {error:?}"); assert_eq!(error.context(), Some(context), "{source}: {error:?}"); let span = error.span().unwrap(); assert_eq!(span.start, start, "{source}: {error:?}"); assert_eq!(span.file, "expressions.wave"); - assert_eq!( - error.line(), - source[..start].bytes().filter(|b| *b == b'\n').count() + 1 - ); - assert_eq!( - error.column(), - source[..start].rsplit('\n').next().unwrap().chars().count() + 1 - ); + assert_eq!(error.line(), source[..start].bytes().filter(|b| *b == b'\n').count() + 1); + assert_eq!(error.column(), source[..start].rsplit('\n').next().unwrap().chars().count() + 1); if span.start < source.len() { - assert!( - error - .found() - .unwrap() - .contains(&source[span.start..span.end]), - "{error:?}" - ); + assert!(error.found().unwrap().contains(&source[span.start..span.end]), "{error:?}"); } else { assert_eq!(error.found(), Some("Eof")); } @@ -55,18 +41,10 @@ fn malformed_postfix_and_aggregate_forms_keep_their_construct_context() { ("Point { x: 1 @y: 2 }", "',' or '}'", "struct literal"), ("call(1, @)", "expression", "primary expression"), ("[1, @]", "expression", "primary expression"), - ( - "call(object.method([Point { x: item.@; }]))", - "identifier", - "member access", - ), + ("call(object.method([Point { x: item.@; }]))", "identifier", "member access"), ("call>(item.@;)", "identifier", "member access"), ] { - failure( - &format!("// 한글\r\nfun f() {{\r\n {expr}\r\n}}"), - expected, - context, - ); + failure(&format!("// 한글\r\nfun f() {{\r\n {expr}\r\n}}"), expected, context); } } diff --git a/front/parser/tests/unclosed_delimiters.rs b/front/parser/tests/unclosed_delimiters.rs index db664b72..5067169f 100644 --- a/front/parser/tests/unclosed_delimiters.rs +++ b/front/parser/tests/unclosed_delimiters.rs @@ -26,30 +26,18 @@ fn unclosed_delimiters_retain_their_opening_span_at_every_depth() { let marked = format!("// 한글\r\n{marked}"); let start = marked.find('@').unwrap(); let source = marked.replace('@', ""); - let tokens = Lexer::new_with_file(&source, "unclosed.wave") - .tokenize() - .unwrap(); + let tokens = Lexer::new_with_file(&source, "unclosed.wave").tokenize().unwrap(); let error = parse_syntax_with_spans(&tokens).unwrap_err(); assert_eq!(error.found(), Some("Eof"), "{source}: {error:?}"); - assert_eq!( - error.span().unwrap().start, - source.len(), - "{source}: {error:?}" - ); + assert_eq!(error.span().unwrap().start, source.len(), "{source}: {error:?}"); assert_eq!(error.related().len(), 1, "{source}: {error:?}"); let related = &error.related()[0]; assert_eq!(related.span.start, start, "{source}: {error:?}"); assert_eq!(related.span.end, start + 1); assert_eq!(related.span.file, "unclosed.wave"); - assert!( - related.message.contains(&format!("expected '{closer}'")), - "{error:?}" - ); + assert!(related.message.contains(&format!("expected '{closer}'")), "{error:?}"); let no_spans = parse_syntax_only(&tokens).unwrap_err(); - assert!(no_spans - .note() - .unwrap() - .contains(&format!("expected '{closer}'"))); + assert!(no_spans.note().unwrap().contains(&format!("expected '{closer}'"))); } } diff --git a/llvm/build.rs b/llvm/build.rs index 09de9289..5b3237b9 100644 --- a/llvm/build.rs +++ b/llvm/build.rs @@ -112,26 +112,18 @@ fn main() { } } - println!( - "cargo:rustc-env=WAVE_BUILD_CRT_DIR={}", - output_root.display() - ); + println!("cargo:rustc-env=WAVE_BUILD_CRT_DIR={}", output_root.display()); } fn build_crt(llvm_mc: &OsString, output_root: &Path, spec: &CrtSpec) { - let source_dir = Path::new(spec.source) - .parent() - .expect("Linux CRT source has an architecture directory"); + let source_dir = + Path::new(spec.source).parent().expect("Linux CRT source has an architecture directory"); let mut output_dir = output_root.join(spec.target); if let Some(abi) = spec.abi { output_dir.push(abi); } fs::create_dir_all(&output_dir).unwrap_or_else(|error| { - panic!( - "failed to create Linux CRT output directory '{}': {}", - output_dir.display(), - error - ) + panic!("failed to create Linux CRT output directory '{}': {}", output_dir.display(), error) }); for (object_name, source_name) in CRT_OBJECTS { @@ -139,10 +131,7 @@ fn build_crt(llvm_mc: &OsString, output_root: &Path, spec: &CrtSpec) { let output = output_dir.join(object_name); let mut command = Command::new(llvm_mc); command - .arg(format!( - "-triple={}", - llvm_triple_for_abi(spec.target, spec.abi) - )) + .arg(format!("-triple={}", llvm_triple_for_abi(spec.target, spec.abi))) .arg("-filetype=obj"); if let Some(attributes) = spec.attributes { command.arg(format!("-mattr={attributes}")); @@ -154,7 +143,7 @@ fn build_crt(llvm_mc: &OsString, output_root: &Path, spec: &CrtSpec) { } let result = command.arg(&source).arg("-o").arg(&output).output(); match result { - Ok(result) if result.status.success() => {} + Ok(result) if result.status.success() => {}, Ok(result) => panic!( "failed to assemble Linux CRT '{}' for '{}': {}", source.display(), @@ -202,17 +191,9 @@ fn find_llvm_mc() -> OsString { } } - OsString::from(if cfg!(windows) { - "llvm-mc.exe" - } else { - "llvm-mc" - }) + OsString::from(if cfg!(windows) { "llvm-mc.exe" } else { "llvm-mc" }) } fn llvm_tool_in(directory: impl AsRef, name: &str) -> PathBuf { - directory.as_ref().join(if cfg!(windows) { - format!("{name}.exe") - } else { - name.to_string() - }) + directory.as_ref().join(if cfg!(windows) { format!("{name}.exe") } else { name.to_string() }) } diff --git a/llvm/src/backend.rs b/llvm/src/backend.rs index fdaa019a..6c6946e1 100644 --- a/llvm/src/backend.rs +++ b/llvm/src/backend.rs @@ -56,16 +56,8 @@ pub fn msvc_link_args( entry: Option<&str>, extra: &[String], ) -> Vec { - let machine = if target.starts_with("aarch64-") { - "ARM64" - } else { - "X64" - }; - let mut args = vec![ - "/NOLOGO".into(), - format!("/MACHINE:{machine}"), - format!("/OUT:{output}"), - ]; + let machine = if target.starts_with("aarch64-") { "ARM64" } else { "X64" }; + let mut args = vec!["/NOLOGO".into(), format!("/MACHINE:{machine}"), format!("/OUT:{output}")]; if shared { args.push("/DLL".into()); } else { @@ -110,9 +102,7 @@ pub fn msvc_link_args( } fn is_wasm_target(target: Option<&str>) -> bool { - target - .and_then(target_spec_for_triple) - .is_some_and(|spec| spec.object_format == "wasm") + target.and_then(target_spec_for_triple).is_some_and(|spec| spec.object_format == "wasm") } fn normalize_llvm_opt_flag(opt_flag: &str) -> &str { @@ -139,10 +129,7 @@ pub fn compile_ir_to_object( configure_bundled_llvm_tool_env(&mut cmd, &llc); if let Some(target) = &backend.target { - cmd.arg(format!( - "--mtriple={}", - llvm_triple_for_abi(target, backend.abi.as_deref()) - )); + cmd.arg(format!("--mtriple={}", llvm_triple_for_abi(target, backend.abi.as_deref()))); } if let Some(cpu) = &backend.cpu { cmd.arg(format!("--mcpu={}", cpu)); @@ -215,10 +202,7 @@ pub fn link_objects( ) -> Result<(), CodegenError> { let pending = PendingOutput::new(std::path::Path::new(output))?; let target = backend.target.as_deref().unwrap_or(""); - let linker_bin = backend - .linker - .clone() - .unwrap_or_else(|| default_lld_for_target(target)); + let linker_bin = backend.linker.clone().unwrap_or_else(|| default_lld_for_target(target)); let mut cmd = Command::new(&linker_bin); configure_bundled_llvm_tool_env(&mut cmd, &linker_bin); @@ -291,9 +275,8 @@ pub fn link_objects( } } - let output = cmd - .output() - .map_err(|e| CodegenError::tool_launch(CodegenPhase::Link, &linker_bin, e))?; + let output = + cmd.output().map_err(|e| CodegenError::tool_launch(CodegenPhase::Link, &linker_bin, e))?; if !output.status.success() { return Err(CodegenError::new( CodegenPhase::Link, @@ -354,9 +337,7 @@ fn expand_lld_link_args(link_args: &[String]) -> Vec { } if let Some(rest) = arg.strip_prefix("-Wl,") { out.extend( - rest.split(',') - .filter(|part| !part.is_empty()) - .map(|part| part.to_string()), + rest.split(',').filter(|part| !part.is_empty()).map(|part| part.to_string()), ); } else { out.push(arg.clone()); diff --git a/llvm/src/codegen/abi_c.rs b/llvm/src/codegen/abi_c.rs index ec3e0d71..9e91ab66 100644 --- a/llvm/src/codegen/abi_c.rs +++ b/llvm/src/codegen/abi_c.rs @@ -87,11 +87,8 @@ fn integer_extension_for_target(target: CodegenTarget, ty: &WaveType) -> Option< } fn riscv_flen(target: CodegenTarget, abi: Option<&str>) -> u64 { - match abi.unwrap_or(if target == CodegenTarget::FreestandingRISCV64 { - "lp64" - } else { - "lp64d" - }) { + match abi.unwrap_or(if target == CodegenTarget::FreestandingRISCV64 { "lp64" } else { "lp64d" }) + { "lp64" => 0, "lp64f" => 4, "lp64d" => 8, @@ -158,10 +155,10 @@ fn classify_ret<'ctx>( | CodegenTarget::LinuxRISCV64 | CodegenTarget::FreestandingRISCV64 => { classify_ret_riscv64(context, td, t, riscv_flen(target, target_abi)) - } + }, CodegenTarget::LinuxLoongArch64 => { classify_ret_loongarch64(context, td, t, loongarch_frlen_bytes(target_abi)) - } + }, CodegenTarget::Wasm32Unknown | CodegenTarget::Wasm32WasiP1 | CodegenTarget::Wasm64Unknown => classify_ret_wasm(td, t), @@ -176,11 +173,7 @@ pub fn lower_extern_c<'ctx>( ext: &ExternFunctionNode, struct_types: &HashMap>, ) -> LoweredExtern<'ctx> { - let llvm_name = ext - .symbol - .as_deref() - .unwrap_or(ext.name.as_str()) - .to_string(); + let llvm_name = ext.symbol.as_deref().unwrap_or(ext.name.as_str()).to_string(); let info_llvm_name = llvm_name.clone(); // wave types -> layout types @@ -192,12 +185,7 @@ pub fn lower_extern_c<'ctx>( let wave_ret_layout: Option> = match &ext.return_type { WaveType::Void | WaveType::Never => None, - ty => Some(wave_type_to_llvm_type( - context, - ty, - struct_types, - TypeFlavor::AbiC, - )), + ty => Some(wave_type_to_llvm_type(context, ty, struct_types, TypeFlavor::AbiC)), }; let ret = classify_ret(context, td, target, wave_ret_layout, target_abi); @@ -210,11 +198,7 @@ pub fn lower_extern_c<'ctx>( | CodegenTarget::FreestandingRISCV64 ) { let flen = riscv_flen(target, target_abi); - let mut gp_left = if matches!(ret, RetLowering::SRet { .. }) { - 7 - } else { - 8 - }; + let mut gp_left = if matches!(ret, RetLowering::SRet { .. }) { 7 } else { 8 }; let mut fp_left = if flen == 0 { 0 } else { 8 }; for param in wave_param_layout { params.push(classify_param_riscv64( @@ -228,11 +212,7 @@ pub fn lower_extern_c<'ctx>( } } else if target == CodegenTarget::LinuxLoongArch64 { let frlen_bytes = loongarch_frlen_bytes(target_abi); - let mut gars_left = if matches!(ret, RetLowering::SRet { .. }) { - 7 - } else { - 8 - }; + let mut gars_left = if matches!(ret, RetLowering::SRet { .. }) { 7 } else { 8 }; let mut fars_left = if frlen_bytes == 0 { 0 } else { 8 }; for param in wave_param_layout { params.push(classify_param_loongarch64( @@ -251,11 +231,7 @@ pub fn lower_extern_c<'ctx>( | CodegenTarget::FreeBsdX86_64 | CodegenTarget::FreestandingX86_64 ) { - let mut gp_left = if matches!(ret, RetLowering::SRet { .. }) { - 5 - } else { - 6 - }; + let mut gp_left = if matches!(ret, RetLowering::SRet { .. }) { 5 } else { 6 }; let mut sse_left = 8; for param in wave_param_layout { params.push(classify_param_sysv_with_registers( @@ -271,11 +247,8 @@ pub fn lower_extern_c<'ctx>( params.push(classify_param(context, td, target, param, ext.variadic)); } } - let param_extensions = ext - .params - .iter() - .map(|(_, ty)| integer_extension_for_target(target, ty)) - .collect(); + let param_extensions = + ext.params.iter().map(|(_, ty)| integer_extension_for_target(target, ty)).collect(); // build lowered param list (sret first, then params possibly split) let mut llvm_param_types: Vec> = vec![]; @@ -288,29 +261,29 @@ pub fn lower_extern_c<'ctx>( for p in ¶ms { match p { - ParamLowering::Ignore => {} + ParamLowering::Ignore => {}, ParamLowering::Direct(t) => llvm_param_types.push((*t).into()), ParamLowering::Split(parts) => { for pt in parts { llvm_param_types.push((*pt).into()); } - } + }, ParamLowering::CoerceAndExpand(parts) => { for part in parts { llvm_param_types.push(part.ty.into()); } - } + }, ParamLowering::Indirect { ty } | ParamLowering::ByVal { ty, .. } => { let ptr = any_ptr_basic(context, ty.clone()); llvm_param_types.push(ptr.into()); - } + }, } } let fn_type = match &ret { RetLowering::Void | RetLowering::SRet { .. } => { context.void_type().fn_type(&llvm_param_types, ext.variadic) - } + }, RetLowering::Direct(t) => t.fn_type(&llvm_param_types, ext.variadic), }; diff --git a/llvm/src/codegen/abi_c/aarch64.rs b/llvm/src/codegen/abi_c/aarch64.rs index a45c89da..7d54e4dc 100644 --- a/llvm/src/codegen/abi_c/aarch64.rs +++ b/llvm/src/codegen/abi_c/aarch64.rs @@ -26,9 +26,7 @@ fn is_homogeneous_float_aggregate<'ctx>(td: &TargetData, t: BasicTypeEnum<'ctx>) let Some(first_size) = is_float_ty(td, leaves[0]) else { return false; }; - leaves - .iter() - .all(|leaf| is_float_ty(td, *leaf) == Some(first_size)) + leaves.iter().all(|leaf| is_float_ty(td, *leaf) == Some(first_size)) } pub(super) fn classify_param_arm64<'ctx>( @@ -38,10 +36,7 @@ pub(super) fn classify_param_arm64<'ctx>( allow_hfa: bool, ) -> ParamLowering<'ctx> { let size = td.get_store_size(&t) as u64; - let is_agg = matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_agg = matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); // AAPCS64 stage B checks HFAs before the general >16-byte rule. Four // doubles still travel in FP registers (or together on stack when exhausted). @@ -49,17 +44,12 @@ pub(super) fn classify_param_arm64<'ctx>( let mut leaves = Vec::new(); flatten_leaf_types(t, &mut leaves); return ParamLowering::Direct( - leaves[0] - .into_float_type() - .array_type(leaves.len() as u32) - .as_basic_type_enum(), + leaves[0].into_float_type().array_type(leaves.len() as u32).as_basic_type_enum(), ); } if is_agg && size > 16 { - return ParamLowering::Indirect { - ty: t.as_any_type_enum(), - }; + return ParamLowering::Indirect { ty: t.as_any_type_enum() }; } if is_agg && size == 0 { @@ -99,10 +89,7 @@ pub(super) fn classify_ret_arm64<'ctx>( return RetLowering::Void; }; let size = td.get_store_size(&t) as u64; - let is_agg = matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_agg = matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); // HFA results use v0-v3 even when their total size exceeds 16 bytes. if is_agg && is_homogeneous_float_aggregate(td, t) { @@ -111,10 +98,7 @@ pub(super) fn classify_ret_arm64<'ctx>( if is_agg && size > 16 { let align = td.get_abi_alignment(&t) as u32; - return RetLowering::SRet { - ty: t.as_any_type_enum(), - align, - }; + return RetLowering::SRet { ty: t.as_any_type_enum(), align }; } if is_agg && size == 0 { @@ -124,9 +108,7 @@ pub(super) fn classify_ret_arm64<'ctx>( if is_agg { if size <= 8 { return RetLowering::Direct( - context - .custom_width_int_type((size * 8) as u32) - .as_basic_type_enum(), + context.custom_width_int_type((size * 8) as u32).as_basic_type_enum(), ); } diff --git a/llvm/src/codegen/abi_c/attributes.rs b/llvm/src/codegen/abi_c/attributes.rs index 998a1f16..5045fd74 100644 --- a/llvm/src/codegen/abi_c/attributes.rs +++ b/llvm/src/codegen/abi_c/attributes.rs @@ -40,10 +40,7 @@ pub fn apply_extern_c_attrs<'ctx>( let mut llvm_param_index: u32 = 0; if let Some(extension) = info.ret_extension { - f.add_attribute( - AttributeLoc::Return, - integer_extension_attr(context, extension), - ); + f.add_attribute(AttributeLoc::Return, integer_extension_attr(context, extension)); } // sret first param @@ -61,7 +58,7 @@ pub fn apply_extern_c_attrs<'ctx>( for (p, extension) in info.params.iter().zip(info.param_extensions.iter()) { match p { - ParamLowering::Ignore => {} + ParamLowering::Ignore => {}, ParamLowering::Direct(_) => { if let Some(extension) = extension { f.add_attribute( @@ -70,16 +67,16 @@ pub fn apply_extern_c_attrs<'ctx>( ); } llvm_param_index += 1; - } + }, ParamLowering::Split(parts) => { llvm_param_index += parts.len() as u32; - } + }, ParamLowering::CoerceAndExpand(parts) => { llvm_param_index += parts.len() as u32; - } + }, ParamLowering::Indirect { .. } => { llvm_param_index += 1; - } + }, ParamLowering::ByVal { ty, align } => { let byval_kind = Attribute::get_named_enum_kind_id("byval"); let byval_attr = context.create_type_attribute(byval_kind, *ty); @@ -90,7 +87,7 @@ pub fn apply_extern_c_attrs<'ctx>( f.add_attribute(AttributeLoc::Param(llvm_param_index), align_attr); llvm_param_index += 1; - } + }, } } } @@ -103,18 +100,12 @@ pub fn apply_extern_c_callsite_attrs<'ctx>( let mut llvm_param_index: u32 = 0; if let Some(extension) = info.ret_extension { - call.add_attribute( - AttributeLoc::Return, - integer_extension_attr(context, extension), - ); + call.add_attribute(AttributeLoc::Return, integer_extension_attr(context, extension)); } if let RetLowering::SRet { ty, align } = &info.ret { let sret_kind = Attribute::get_named_enum_kind_id("sret"); - call.add_attribute( - AttributeLoc::Param(0), - context.create_type_attribute(sret_kind, *ty), - ); + call.add_attribute(AttributeLoc::Param(0), context.create_type_attribute(sret_kind, *ty)); let align_kind = Attribute::get_named_enum_kind_id("align"); call.add_attribute( @@ -127,7 +118,7 @@ pub fn apply_extern_c_callsite_attrs<'ctx>( for (p, extension) in info.params.iter().zip(info.param_extensions.iter()) { match p { - ParamLowering::Ignore => {} + ParamLowering::Ignore => {}, ParamLowering::Direct(_) => { if let Some(extension) = extension { call.add_attribute( @@ -136,16 +127,16 @@ pub fn apply_extern_c_callsite_attrs<'ctx>( ); } llvm_param_index += 1; - } + }, ParamLowering::Split(parts) => { llvm_param_index += parts.len() as u32; - } + }, ParamLowering::CoerceAndExpand(parts) => { llvm_param_index += parts.len() as u32; - } + }, ParamLowering::Indirect { .. } => { llvm_param_index += 1; - } + }, ParamLowering::ByVal { ty, align } => { let byval_kind = Attribute::get_named_enum_kind_id("byval"); call.add_attribute( @@ -160,7 +151,7 @@ pub fn apply_extern_c_callsite_attrs<'ctx>( ); llvm_param_index += 1; - } + }, } } } diff --git a/llvm/src/codegen/abi_c/loongarch.rs b/llvm/src/codegen/abi_c/loongarch.rs index bdc1ede2..e8a117c5 100644 --- a/llvm/src/codegen/abi_c/loongarch.rs +++ b/llvm/src/codegen/abi_c/loongarch.rs @@ -46,7 +46,7 @@ fn flatten_loongarch_fields<'ctx>( } } true - } + }, BasicTypeEnum::ArrayType(array_ty) => { let element = array_ty.get_element_type(); let stride = td.get_abi_size(&element); @@ -62,23 +62,17 @@ fn flatten_loongarch_fields<'ctx>( } } true - } + }, BasicTypeEnum::IntType(int_ty) if int_ty.get_bit_width() <= 64 => { - fields.push(AbiPart { - ty, - offset: base_offset, - }); + fields.push(AbiPart { ty, offset: base_offset }); fields.len() <= 2 - } + }, BasicTypeEnum::FloatType(_) if frlen_bytes != 0 && td.get_store_size(&ty) <= frlen_bytes => { - fields.push(AbiPart { - ty, - offset: base_offset, - }); + fields.push(AbiPart { ty, offset: base_offset }); fields.len() <= 2 - } + }, _ => false, } } @@ -95,10 +89,8 @@ fn loongarch_fars_eligible_struct<'ctx>( if !flatten_loongarch_fields(td, ty, 0, frlen_bytes, &mut fields) || fields.is_empty() { return None; } - let fars = fields - .iter() - .filter(|field| matches!(field.ty, BasicTypeEnum::FloatType(_))) - .count(); + let fars = + fields.iter().filter(|field| matches!(field.ty, BasicTypeEnum::FloatType(_))).count(); let gars = fields.len() - fars; if fars == 0 || gars > 1 { return None; @@ -136,10 +128,7 @@ pub(super) fn classify_param_loongarch64<'ctx>( fars_left: &mut usize, ) -> ParamLowering<'ctx> { let size = td.get_store_size(&ty); - let is_aggregate = matches!( - ty, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_aggregate = matches!(ty, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if is_aggregate && size == 0 { return ParamLowering::Ignore; } @@ -165,9 +154,7 @@ pub(super) fn classify_param_loongarch64<'ctx>( consume_loongarch_gars(td, ty, gars_left); if is_aggregate && size > 16 { - return ParamLowering::Indirect { - ty: ty.as_any_type_enum(), - }; + return ParamLowering::Indirect { ty: ty.as_any_type_enum() }; } if is_aggregate { return if size <= 8 { @@ -189,10 +176,7 @@ pub(super) fn classify_ret_loongarch64<'ctx>( return RetLowering::Void; }; let size = td.get_store_size(&ty); - let is_aggregate = matches!( - ty, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_aggregate = matches!(ty, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if is_aggregate && size == 0 { return RetLowering::Void; } @@ -205,19 +189,13 @@ pub(super) fn classify_ret_loongarch64<'ctx>( } return RetLowering::Direct( context - .struct_type( - &fields.iter().map(|field| field.ty).collect::>(), - false, - ) + .struct_type(&fields.iter().map(|field| field.ty).collect::>(), false) .as_basic_type_enum(), ); } } if is_aggregate && size > 16 { - return RetLowering::SRet { - ty: ty.as_any_type_enum(), - align: td.get_abi_alignment(&ty), - }; + return RetLowering::SRet { ty: ty.as_any_type_enum(), align: td.get_abi_alignment(&ty) }; } if is_aggregate { return if size <= 8 { diff --git a/llvm/src/codegen/abi_c/riscv.rs b/llvm/src/codegen/abi_c/riscv.rs index 1ea1fe64..8e8b8488 100644 --- a/llvm/src/codegen/abi_c/riscv.rs +++ b/llvm/src/codegen/abi_c/riscv.rs @@ -41,7 +41,7 @@ fn float_fields<'ctx>( return false; } } - } + }, BasicTypeEnum::ArrayType(array) => { let element = array.get_element_type(); for i in 0..array.len() { @@ -55,13 +55,13 @@ fn float_fields<'ctx>( return false; } } - } + }, BasicTypeEnum::FloatType(_) if flen > 0 && td.get_store_size(&ty) <= flen => { fields.push(AbiPart { ty, offset }) - } + }, BasicTypeEnum::IntType(integer) if integer.get_bit_width() <= 64 => { fields.push(AbiPart { ty, offset }) - } + }, // The hardware FP convention accepts integer fields, not pointer fields. BasicTypeEnum::PointerType(_) => return false, _ => return false, @@ -81,10 +81,7 @@ fn eligible<'ctx>( if !float_fields(td, ty, 0, flen, &mut fields) { return None; } - let fp = fields - .iter() - .filter(|f| matches!(f.ty, BasicTypeEnum::FloatType(_))) - .count(); + let fp = fields.iter().filter(|f| matches!(f.ty, BasicTypeEnum::FloatType(_))).count(); let gp = fields.len() - fp; (fp > 0 && gp <= 1).then_some((fields, gp, fp)) } @@ -98,10 +95,7 @@ pub(super) fn classify_param_riscv64<'ctx>( fp_left: &mut usize, ) -> ParamLowering<'ctx> { let size = td.get_store_size(&ty); - let aggregate = matches!( - ty, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let aggregate = matches!(ty, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if aggregate && size == 0 { return ParamLowering::Ignore; } @@ -119,9 +113,7 @@ pub(super) fn classify_param_riscv64<'ctx>( *gp_left = gp_left.saturating_sub(if size > 8 && size <= 16 { 2 } else { 1 }); if aggregate { if size > 16 { - return ParamLowering::Indirect { - ty: ty.as_any_type_enum(), - }; + return ParamLowering::Indirect { ty: ty.as_any_type_enum() }; } return ParamLowering::Direct(if size <= 8 { context.i64_type().as_basic_type_enum() @@ -142,10 +134,7 @@ pub(super) fn classify_ret_riscv64<'ctx>( return RetLowering::Void; }; let size = td.get_store_size(&ty); - let aggregate = matches!( - ty, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let aggregate = matches!(ty, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if aggregate && size == 0 { return RetLowering::Void; } diff --git a/llvm/src/codegen/abi_c/shared.rs b/llvm/src/codegen/abi_c/shared.rs index 11a65da1..7f955701 100644 --- a/llvm/src/codegen/abi_c/shared.rs +++ b/llvm/src/codegen/abi_c/shared.rs @@ -116,13 +116,13 @@ pub(super) fn flatten_leaf_types<'ctx>(t: BasicTypeEnum<'ctx>, out: &mut Vec { let elem = at.get_element_type(); for _ in 0..at.len() { flatten_leaf_types(elem, out); } - } + }, _ => out.push(t), } } diff --git a/llvm/src/codegen/abi_c/sysv.rs b/llvm/src/codegen/abi_c/sysv.rs index 0309c94a..e7db99fb 100644 --- a/llvm/src/codegen/abi_c/sysv.rs +++ b/llvm/src/codegen/abi_c/sysv.rs @@ -24,32 +24,17 @@ pub(super) fn classify_param_x86_64_sysv<'ctx>( let size = td.get_store_size(&t) as u64; // large aggregates => byval - if matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ) && size > 16 - { + if matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)) && size > 16 { let align = td.get_abi_alignment(&t) as u32; - return ParamLowering::ByVal { - ty: t.as_any_type_enum(), - align, - }; + return ParamLowering::ByVal { ty: t.as_any_type_enum(), align }; } - if matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ) && size == 0 - { + if matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)) && size == 0 { return ParamLowering::Ignore; } // small aggregates: try integer-only or homogeneous float - if matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ) && size <= 16 - { + if matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)) && size <= 16 { let mut leaves = vec![]; flatten_leaf_types(t, &mut leaves); @@ -93,11 +78,8 @@ pub(super) fn classify_param_x86_64_sysv<'ctx>( ]), _ => { let align = td.get_abi_alignment(&t) as u32; - ParamLowering::ByVal { - ty: t.as_any_type_enum(), - align, - } - } + ParamLowering::ByVal { ty: t.as_any_type_enum(), align } + }, }; } else if fsz == 8 { let f = context.f64_type(); @@ -106,11 +88,8 @@ pub(super) fn classify_param_x86_64_sysv<'ctx>( 2 => ParamLowering::Split(vec![f.as_basic_type_enum(), f.as_basic_type_enum()]), _ => { let align = td.get_abi_alignment(&t) as u32; - ParamLowering::ByVal { - ty: t.as_any_type_enum(), - align, - } - } + ParamLowering::ByVal { ty: t.as_any_type_enum(), align } + }, }; } } @@ -119,11 +98,11 @@ pub(super) fn classify_param_x86_64_sysv<'ctx>( let mut all_intlike = true; for lt in &leaves { match lt { - BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => {} + BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => {}, _ => { all_intlike = false; break; - } + }, } } if all_intlike { @@ -158,9 +137,7 @@ pub(super) fn classify_param_x86_64_sysv<'ctx>( } let bytes = (size - offset).min(8); let ty = if integer { - context - .custom_width_int_type(bytes as u32 * 8) - .as_basic_type_enum() + context.custom_width_int_type(bytes as u32 * 8).as_basic_type_enum() } else if bytes <= 4 { context.f32_type().as_basic_type_enum() } else { @@ -192,7 +169,7 @@ fn sysv_eightbyte_classes( classes, ); } - } + }, BasicTypeEnum::ArrayType(array) => { let element = array.get_element_type(); for index in 0..array.len() { @@ -203,21 +180,18 @@ fn sysv_eightbyte_classes( classes, ); } - } + }, _ => { let end = offset + td.get_store_size(&ty); for byte in offset..end { let class = &mut classes[(byte / 8) as usize]; - if matches!( - ty, - BasicTypeEnum::FloatType(_) | BasicTypeEnum::VectorType(_) - ) { + if matches!(ty, BasicTypeEnum::FloatType(_) | BasicTypeEnum::VectorType(_)) { class.1 = true; } else { class.0 = true; } } - } + }, } } @@ -240,20 +214,17 @@ pub(super) fn classify_param_sysv_with_registers<'ctx>( let lowering = classify_param_x86_64_sysv(context, td, ty); let (gp, sse) = match &lowering { ParamLowering::Direct(ty) => sysv_registers(*ty), - ParamLowering::Split(parts) => parts - .iter() - .map(|ty| sysv_registers(*ty)) - .fold((0, 0), |a, b| (a.0 + b.0, a.1 + b.1)), + ParamLowering::Split(parts) => { + parts.iter().map(|ty| sysv_registers(*ty)).fold((0, 0), |a, b| (a.0 + b.0, a.1 + b.1)) + }, ParamLowering::CoerceAndExpand(parts) => parts .iter() .map(|part| sysv_registers(part.ty)) .fold((0, 0), |a, b| (a.0 + b.0, a.1 + b.1)), _ => (0, 0), }; - if matches!( - ty, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ) && (gp > *gp_left || sse > *sse_left) + if matches!(ty, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)) + && (gp > *gp_left || sse > *sse_left) { // Roll back the entire aggregate so later arguments can use the // remaining registers. A byval stack slot consumes neither budget. @@ -277,17 +248,11 @@ pub(super) fn classify_ret_x86_64_sysv<'ctx>( }; let size = td.get_store_size(&t) as u64; - let is_agg = matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_agg = matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if is_agg && size > 16 { let align = td.get_abi_alignment(&t) as u32; - return RetLowering::SRet { - ty: t.as_any_type_enum(), - align, - }; + return RetLowering::SRet { ty: t.as_any_type_enum(), align }; } if is_agg && size == 0 { @@ -308,11 +273,11 @@ pub(super) fn classify_ret_x86_64_sysv<'ctx>( let mut all_intlike = true; for lt in &leaves { match lt { - BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => {} + BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) => {}, _ => { all_intlike = false; break; - } + }, } } if all_intlike { @@ -363,7 +328,7 @@ pub(super) fn classify_ret_x86_64_sysv<'ctx>( ) .as_basic_type_enum(); RetLowering::Direct(tuple) - } + }, 4 => { let tuple = context .struct_type( @@ -375,14 +340,11 @@ pub(super) fn classify_ret_x86_64_sysv<'ctx>( ) .as_basic_type_enum(); RetLowering::Direct(tuple) - } + }, _ => { let align = td.get_abi_alignment(&t) as u32; - RetLowering::SRet { - ty: t.as_any_type_enum(), - align, - } - } + RetLowering::SRet { ty: t.as_any_type_enum(), align } + }, }; } if float_kind == Some(8) { @@ -394,14 +356,11 @@ pub(super) fn classify_ret_x86_64_sysv<'ctx>( .struct_type(&[f.as_basic_type_enum(), f.as_basic_type_enum()], false) .as_basic_type_enum(); RetLowering::Direct(tuple) - } + }, _ => { let align = td.get_abi_alignment(&t) as u32; - RetLowering::SRet { - ty: t.as_any_type_enum(), - align, - } - } + RetLowering::SRet { ty: t.as_any_type_enum(), align } + }, }; } } diff --git a/llvm/src/codegen/abi_c/wasm.rs b/llvm/src/codegen/abi_c/wasm.rs index a4b1adbe..02434e63 100644 --- a/llvm/src/codegen/abi_c/wasm.rs +++ b/llvm/src/codegen/abi_c/wasm.rs @@ -25,10 +25,7 @@ pub(super) fn classify_param_wasm<'ctx>( td: &TargetData, t: BasicTypeEnum<'ctx>, ) -> ParamLowering<'ctx> { - let is_aggregate = matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_aggregate = matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if !is_aggregate { return ParamLowering::Direct(t); } @@ -38,10 +35,7 @@ pub(super) fn classify_param_wasm<'ctx>( if let Some(leaf) = wasm_single_leaf(t) { return ParamLowering::Direct(leaf); } - ParamLowering::ByVal { - ty: t.as_any_type_enum(), - align: td.get_abi_alignment(&t), - } + ParamLowering::ByVal { ty: t.as_any_type_enum(), align: td.get_abi_alignment(&t) } } pub(super) fn classify_ret_wasm<'ctx>( @@ -51,10 +45,7 @@ pub(super) fn classify_ret_wasm<'ctx>( let Some(t) = t else { return RetLowering::Void; }; - let is_aggregate = matches!( - t, - BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) - ); + let is_aggregate = matches!(t, BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)); if !is_aggregate { return RetLowering::Direct(t); } @@ -64,8 +55,5 @@ pub(super) fn classify_ret_wasm<'ctx>( if let Some(leaf) = wasm_single_leaf(t) { return RetLowering::Direct(leaf); } - RetLowering::SRet { - ty: t.as_any_type_enum(), - align: td.get_abi_alignment(&t), - } + RetLowering::SRet { ty: t.as_any_type_enum(), align: td.get_abi_alignment(&t) } } diff --git a/llvm/src/codegen/abi_c/windows.rs b/llvm/src/codegen/abi_c/windows.rs index 362437d1..c3a55311 100644 --- a/llvm/src/codegen/abi_c/windows.rs +++ b/llvm/src/codegen/abi_c/windows.rs @@ -27,13 +27,9 @@ pub(super) fn classify_param_x86_64_windows<'ctx>( BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) => match size { 0 => ParamLowering::Ignore, 1 | 2 | 4 | 8 => ParamLowering::Direct( - context - .custom_width_int_type((size * 8) as u32) - .as_basic_type_enum(), + context.custom_width_int_type((size * 8) as u32).as_basic_type_enum(), ), - _ => ParamLowering::Indirect { - ty: t.as_any_type_enum(), - }, + _ => ParamLowering::Indirect { ty: t.as_any_type_enum() }, }, _ => ParamLowering::Direct(t), } @@ -53,9 +49,7 @@ pub(super) fn classify_ret_x86_64_windows<'ctx>( BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) => match size { 0 => RetLowering::Void, 1 | 2 | 4 | 8 => RetLowering::Direct( - context - .custom_width_int_type((size * 8) as u32) - .as_basic_type_enum(), + context.custom_width_int_type((size * 8) as u32).as_basic_type_enum(), ), _ => RetLowering::SRet { ty: t.as_any_type_enum(), diff --git a/llvm/src/codegen/address.rs b/llvm/src/codegen/address.rs index 2a83fb19..70387190 100644 --- a/llvm/src/codegen/address.rs +++ b/llvm/src/codegen/address.rs @@ -35,9 +35,7 @@ use crate::codegen::wave_type_to_llvm_type; use super::types::VariableInfo; fn normalize_struct_name(raw: &str) -> &str { - raw.strip_prefix("struct.") - .unwrap_or(raw) - .trim_start_matches('%') + raw.strip_prefix("struct.").unwrap_or(raw).trim_start_matches('%') } /// Evaluate once in the expression's own integer type, then adapt the offset @@ -57,24 +55,17 @@ pub(crate) fn generate_index_ir<'ctx>( env.wave_type(expr), Some(WaveType::Uint(_) | WaveType::Byte | WaveType::Char | WaveType::Bool) ); - match value - .get_type() - .get_bit_width() - .cmp(&index_ty.get_bit_width()) - { + match value.get_type().get_bit_width().cmp(&index_ty.get_bit_width()) { std::cmp::Ordering::Equal => value, - std::cmp::Ordering::Less if source_unsigned => env - .builder - .build_int_z_extend(value, index_ty, "idx_zext") - .unwrap(), - std::cmp::Ordering::Less => env - .builder - .build_int_s_extend(value, index_ty, "idx_sext") - .unwrap(), - std::cmp::Ordering::Greater => env - .builder - .build_int_truncate(value, index_ty, "idx_trunc") - .unwrap(), + std::cmp::Ordering::Less if source_unsigned => { + env.builder.build_int_z_extend(value, index_ty, "idx_zext").unwrap() + }, + std::cmp::Ordering::Less => { + env.builder.build_int_s_extend(value, index_ty, "idx_sext").unwrap() + }, + std::cmp::Ordering::Greater => { + env.builder.build_int_truncate(value, index_ty, "idx_trunc").unwrap() + }, } } @@ -112,10 +103,7 @@ fn load_ptr_from_slot<'ctx>( ) -> PointerValue<'ctx> { let aspace = slot_ptr.get_type().get_address_space(); let ptr_ty = context.ptr_type(aspace); - builder - .build_load(ptr_ty, slot_ptr, name) - .unwrap() - .into_pointer_value() + builder.build_load(ptr_ty, slot_ptr, name).unwrap().into_pointer_value() } fn pointee_ty_of_ptr_expr<'ctx>( @@ -132,24 +120,19 @@ fn pointee_ty_of_ptr_expr<'ctx>( match expr { Expression::Grouped(inner) => { pointee_ty_of_ptr_expr(context, inner, program, variables, struct_types) - } + }, Expression::Variable(name) => { - let vi = variables - .get(name) - .unwrap_or_else(|| panic!("Variable {} not found", name)); + let vi = variables.get(name).unwrap_or_else(|| panic!("Variable {} not found", name)); match &vi.ty { WaveType::Pointer(inner) => { wave_type_to_llvm_type(context, inner, struct_types, TypeFlavor::AbiC) - } + }, WaveType::String => context.i8_type().as_basic_type_enum(), - other => panic!( - "deref/index expects pointer type, got {:?} for {}", - other, name - ), + other => panic!("deref/index expects pointer type, got {:?} for {}", other, name), } - } + }, // ptr coming from field/index: LLVM pointer is opaque -> pointee unknown _ => context.i8_type().as_basic_type_enum(), @@ -172,29 +155,21 @@ fn struct_ty_of_ptr_expr<'ctx>( match expr { Expression::Grouped(inner) => { struct_ty_of_ptr_expr(context, inner, program, variables, struct_types) - } + }, Expression::Variable(name) => { - let vi = variables - .get(name) - .unwrap_or_else(|| panic!("Variable {} not found", name)); + let vi = variables.get(name).unwrap_or_else(|| panic!("Variable {} not found", name)); match &vi.ty { WaveType::Pointer(inner) => match inner.as_ref() { WaveType::Struct(sname) => *struct_types .get(sname) .unwrap_or_else(|| panic!("Struct type '{}' not found", sname)), - other => panic!( - "pointer does not point to struct: {:?} (var {})", - other, name - ), + other => panic!("pointer does not point to struct: {:?} (var {})", other, name), }, - other => panic!( - "expected pointer-to-struct var, got {:?} (var {})", - other, name - ), + other => panic!("expected pointer-to-struct var, got {:?} (var {})", other, name), } - } + }, // ptr coming from field/index is opaque; we can't know struct type here without field WaveType info other => panic!( @@ -211,19 +186,14 @@ fn addr_and_ty<'ctx>( expr: &Expression, ) -> (PointerValue<'ctx>, BasicTypeEnum<'ctx>) { match expr { - Expression::Cast { - expr: inner, - target_type: WaveType::Pointer(_), - } + Expression::Cast { expr: inner, target_type: WaveType::Pointer(_) } | Expression::Grouped(inner) => addr_and_ty(env, inner), Expression::Variable(name) => { - let vi = env - .variables - .get(name) - .unwrap_or_else(|| panic!("Variable {} not found", name)); + let vi = + env.variables.get(name).unwrap_or_else(|| panic!("Variable {} not found", name)); (vi.ptr, storage_ty_of_var(env.context, vi, env.struct_types)) - } + }, // legacy behavior: treat &x as "address of x" when someone asks for address again Expression::AddressOf(inner) => addr_and_ty(env, inner), @@ -246,7 +216,7 @@ fn addr_and_ty<'ctx>( env.struct_types, ); (pointer, pointee) - } + }, Expression::FieldAccess { object, field } => { let (obj_addr, obj_ty) = addr_and_ty(env, object); @@ -265,7 +235,7 @@ fn addr_and_ty<'ctx>( env.struct_types, ); (p, st) - } + }, other => panic!("FieldAccess on non-struct object type: {:?}", other), }; @@ -277,23 +247,18 @@ fn addr_and_ty<'ctx>( .unwrap_or_else(|| panic!("Struct '{}' missing in struct_field_indices", sname)) .get(field) .unwrap_or_else(|| { - panic!( - "Field '{}.{}' missing in struct_field_indices", - sname, field - ) + panic!("Field '{}.{}' missing in struct_field_indices", sname, field) }); let field_ty = struct_ty .get_field_type_at_index(idx) .unwrap_or_else(|| panic!("No field type at index {} for struct '{}'", idx, sname)); - let field_ptr = env - .builder - .build_struct_gep(struct_ty, struct_ptr, idx, "field_ptr") - .unwrap(); + let field_ptr = + env.builder.build_struct_gep(struct_ty, struct_ptr, idx, "field_ptr").unwrap(); (field_ptr, field_ty) - } + }, Expression::IndexAccess { target, index } => { let (t_addr, t_ty) = addr_and_ty(env, target); @@ -309,7 +274,7 @@ fn addr_and_ty<'ctx>( .unwrap() }; (ep, at.get_element_type()) - } + }, BasicTypeEnum::PointerType(_) => { let base_ptr = @@ -339,11 +304,11 @@ fn addr_and_ty<'ctx>( }; (ep, pointee) } - } + }, other => panic!("IndexAccess on non-array/non-pointer: {:?}", other), } - } + }, other => panic!("Cannot take address of this expression: {:?}", other), } diff --git a/llvm/src/codegen/arch/aarch64.rs b/llvm/src/codegen/arch/aarch64.rs index c7ba3bc9..358bfa9d 100644 --- a/llvm/src/codegen/arch/aarch64.rs +++ b/llvm/src/codegen/arch/aarch64.rs @@ -34,7 +34,7 @@ pub(crate) fn register_group(token: &str) -> Option { "ip1" => return Some("x17".to_string()), "sp" => return Some("sp".to_string()), "xzr" | "wzr" => return Some("xzr".to_string()), - _ => {} + _ => {}, } let (prefix, num) = token.split_at_checked(1)?; if !matches!(prefix, "x" | "w") || num.is_empty() || !num.chars().all(|c| c.is_ascii_digit()) { @@ -66,10 +66,7 @@ pub(crate) fn default_clobbers() -> Vec { } pub(crate) fn allocatable_registers() -> Vec { - (0..=30) - .filter(|number| *number != 18) - .map(|number| format!("x{}", number)) - .collect() + (0..=30).filter(|number| *number != 18).map(|number| format!("x{}", number)).collect() } pub(crate) fn normalize_special_clobber(token: &str) -> Option { diff --git a/llvm/src/codegen/arch/loongarch64.rs b/llvm/src/codegen/arch/loongarch64.rs index d6d5dd26..00290d9f 100644 --- a/llvm/src/codegen/arch/loongarch64.rs +++ b/llvm/src/codegen/arch/loongarch64.rs @@ -72,7 +72,7 @@ pub(crate) fn register_group(token: &str) -> Option { return None; } raw.parse::().ok()? - } + }, }; (number <= 31).then(|| format!("r{number}")) } @@ -117,7 +117,7 @@ fn floating_register_number(token: &str) -> Option { return None; } raw.parse::().ok()? - } + }, }; (number <= 31).then_some(number) } diff --git a/llvm/src/codegen/arch/mod.rs b/llvm/src/codegen/arch/mod.rs index e7f4a826..2aca4406 100644 --- a/llvm/src/codegen/arch/mod.rs +++ b/llvm/src/codegen/arch/mod.rs @@ -130,10 +130,7 @@ pub(crate) fn instruction_text(line: &str, hash_is_comment: bool) -> String { // Callers choose the rule before labels and mnemonics are normalized. let without_slash_comment = line.split_once("//").map(|(code, _)| code).unwrap_or(line); let line = if hash_is_comment { - without_slash_comment - .split_once('#') - .map(|(code, _)| code) - .unwrap_or(without_slash_comment) + without_slash_comment.split_once('#').map(|(code, _)| code).unwrap_or(without_slash_comment) } else { without_slash_comment }; @@ -142,9 +139,7 @@ pub(crate) fn instruction_text(line: &str, hash_is_comment: bool) -> String { while let Some((label, rest)) = code.split_once(':') { let label = label.trim(); if label.is_empty() - || !label - .chars() - .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.') + || !label.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.') { break; } @@ -154,9 +149,7 @@ pub(crate) fn instruction_text(line: &str, hash_is_comment: bool) -> String { } pub(crate) fn mnemonic(code: &str) -> &str { - code.split(|c: char| c.is_ascii_whitespace() || c == ';') - .next() - .unwrap_or("") + code.split(|c: char| c.is_ascii_whitespace() || c == ';').next().unwrap_or("") } pub(crate) fn stack_analysis(architecture: Architecture, line: &str) -> StackAnalysis { @@ -177,35 +170,17 @@ mod tests { #[test] fn register_aliases_and_widths_are_architecture_local() { - assert_eq!( - register_group(Architecture::X86_64, "%eax").as_deref(), - None - ); - assert_eq!( - register_group(Architecture::X86_64, "eax").as_deref(), - Some("rax") - ); + assert_eq!(register_group(Architecture::X86_64, "%eax").as_deref(), None); + assert_eq!(register_group(Architecture::X86_64, "eax").as_deref(), Some("rax")); assert_eq!(register_width_bits(Architecture::X86_64, "r8d"), Some(32)); - assert_eq!( - register_group(Architecture::Aarch64, "fp").as_deref(), - Some("x29") - ); + assert_eq!(register_group(Architecture::Aarch64, "fp").as_deref(), Some("x29")); assert_eq!(register_width_bits(Architecture::Aarch64, "w30"), Some(32)); - assert_eq!( - register_group(Architecture::Riscv64, "a7").as_deref(), - Some("x17") - ); - assert_eq!( - register_group(Architecture::Riscv64, "fp").as_deref(), - Some("x8") - ); + assert_eq!(register_group(Architecture::Riscv64, "a7").as_deref(), Some("x17")); + assert_eq!(register_group(Architecture::Riscv64, "fp").as_deref(), Some("x8")); assert_eq!(register_width_bits(Architecture::Riscv64, "x31"), Some(64)); - assert_eq!( - operand_register_group(Architecture::Riscv64, "fa0").as_deref(), - Some("f10") - ); + assert_eq!(operand_register_group(Architecture::Riscv64, "fa0").as_deref(), Some("f10")); for reserved in ["zero", "x0", "sp", "x2", "gp", "x3", "tp", "x4"] { assert_eq!( operand_register_group(Architecture::Riscv64, reserved), @@ -215,18 +190,9 @@ mod tests { ); } - assert_eq!( - register_group(Architecture::LoongArch64, "a0").as_deref(), - Some("r4") - ); - assert_eq!( - register_group(Architecture::LoongArch64, "fp").as_deref(), - Some("r22") - ); - assert_eq!( - operand_register_group(Architecture::LoongArch64, "fa0").as_deref(), - Some("f0") - ); + assert_eq!(register_group(Architecture::LoongArch64, "a0").as_deref(), Some("r4")); + assert_eq!(register_group(Architecture::LoongArch64, "fp").as_deref(), Some("r22")); + assert_eq!(operand_register_group(Architecture::LoongArch64, "fa0").as_deref(), Some("f0")); for reserved in ["zero", "r0", "tp", "r2", "sp", "r3", "r21"] { assert_eq!( operand_register_group(Architecture::LoongArch64, reserved), @@ -265,13 +231,7 @@ mod tests { #[test] fn architecture_comment_rules_preserve_aarch64_immediates() { - assert_eq!( - instruction_text("add x0, x0, #1 // note", false), - "add x0, x0, #1" - ); - assert_eq!( - instruction_text("addi a0, a0, 1 # note", true), - "addi a0, a0, 1" - ); + assert_eq!(instruction_text("add x0, x0, #1 // note", false), "add x0, x0, #1"); + assert_eq!(instruction_text("addi a0, a0, 1 # note", true), "addi a0, a0, 1"); } } diff --git a/llvm/src/codegen/arch/riscv64.rs b/llvm/src/codegen/arch/riscv64.rs index 59ceb398..9b01683b 100644 --- a/llvm/src/codegen/arch/riscv64.rs +++ b/llvm/src/codegen/arch/riscv64.rs @@ -107,7 +107,7 @@ pub(crate) fn register_group(token: &str) -> Option { return None; } raw.parse::().ok()? - } + }, }; (number <= 31).then(|| format!("x{}", number)) } @@ -152,7 +152,7 @@ fn floating_register_number(token: &str) -> Option { return None; } raw.parse::().ok()? - } + }, }; (number <= 31).then_some(number) } diff --git a/llvm/src/codegen/arch/x86_64.rs b/llvm/src/codegen/arch/x86_64.rs index 57ae44a1..4739f147 100644 --- a/llvm/src/codegen/arch/x86_64.rs +++ b/llvm/src/codegen/arch/x86_64.rs @@ -68,20 +68,14 @@ pub(crate) fn operand_register_group(token: &str) -> Option { } pub(crate) fn default_clobbers() -> Vec { - ["~{memory}", "~{dirflag}", "~{fpsr}", "~{flags}"] - .into_iter() - .map(String::from) - .collect() + ["~{memory}", "~{dirflag}", "~{fpsr}", "~{flags}"].into_iter().map(String::from).collect() } pub(crate) fn allocatable_registers() -> Vec { - [ - "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", - "r15", - ] - .into_iter() - .map(String::from) - .collect() + ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] + .into_iter() + .map(String::from) + .collect() } pub(crate) fn normalize_special_clobber(token: &str) -> Option { @@ -95,11 +89,8 @@ pub(crate) fn normalize_special_clobber(token: &str) -> Option { } fn parse_immediate(raw: &str) -> Option { - let mut value = raw - .trim() - .trim_start_matches('$') - .trim_start_matches('#') - .trim_end_matches(','); + let mut value = + raw.trim().trim_start_matches('$').trim_start_matches('#').trim_end_matches(','); let negative = value.starts_with('-'); if negative { value = value.trim_start_matches('-'); @@ -114,9 +105,8 @@ fn parse_immediate(raw: &str) -> Option { } fn stack_adjustment(code: &str) -> Option { - let mut parts = code - .split(|c: char| c.is_ascii_whitespace() || c == ',') - .filter(|part| !part.is_empty()); + let mut parts = + code.split(|c: char| c.is_ascii_whitespace() || c == ',').filter(|part| !part.is_empty()); let op = parts.next()?; let first = parts.next()?; let second = parts.next()?; @@ -155,31 +145,29 @@ pub(crate) fn stack_analysis(line: &str) -> super::StackAnalysis { "push" | "pushq" => { out.touches_stack = true; out.unbalanced_delta = -8; - } + }, "pop" | "popq" | "ret" | "retq" => { out.touches_stack = true; out.unbalanced_delta = 8; - } + }, "retf" | "retfq" => { out.touches_stack = true; out.unbalanced_delta = 16; - } + }, "iret" | "iretq" | "leave" | "enter" => { out.touches_stack = true; out.unknown_stack_write = true; - } + }, "jmp" | "jmpq" => out.nonreturning_branch = jump_is_indirect(&code), _ => { if let Some(delta) = stack_adjustment(&code) { out.touches_stack = true; out.unbalanced_delta = delta; } else { - let writes_sp = ["mov", "movq", "and", "andq", "xor", "lea"] - .iter() - .any(|op| { - code.starts_with(&format!("{} rsp", op)) - || code.starts_with(&format!("{} %rsp", op)) - }); + let writes_sp = ["mov", "movq", "and", "andq", "xor", "lea"].iter().any(|op| { + code.starts_with(&format!("{} rsp", op)) + || code.starts_with(&format!("{} %rsp", op)) + }); if writes_sp { out.touches_stack = true; out.unknown_stack_write = true; @@ -191,7 +179,7 @@ pub(crate) fn stack_analysis(line: &str) -> super::StackAnalysis { || code.contains(", sp"); } } - } + }, } out } diff --git a/llvm/src/codegen/arithmetic_runtime/mod.rs b/llvm/src/codegen/arithmetic_runtime/mod.rs index bfe1f154..bbcd56b0 100644 --- a/llvm/src/codegen/arithmetic_runtime/mod.rs +++ b/llvm/src/codegen/arithmetic_runtime/mod.rs @@ -52,28 +52,18 @@ pub(super) fn lower<'ctx>( helper.set_triple(&module.get_triple()); helper.set_data_layout(&module.get_data_layout()); module.link_in_module(helper).map_err(|e| e.to_string())?; - let function = module - .get_function(&name) - .expect("linked arithmetic helper"); + let function = module.get_function(&name).expect("linked arithmetic helper"); function.set_linkage(Linkage::Private); helpers.insert(key.clone(), function); function }; builder.position_before(&instruction); - let arguments: Vec<_> = instruction - .get_operands() - .map(|op| op.unwrap().unwrap_value().into()) - .collect(); - let call = builder - .build_call(function, &arguments, "runtime") - .map_err(|e| e.to_string())?; + let arguments: Vec<_> = + instruction.get_operands().map(|op| op.unwrap().unwrap_value().into()).collect(); + let call = + builder.build_call(function, &arguments, "runtime").map_err(|e| e.to_string())?; instruction.replace_all_uses_with( - &call - .try_as_basic_value() - .basic() - .unwrap() - .as_instruction_value() - .unwrap(), + &call.try_as_basic_value().basic().unwrap().as_instruction_value().unwrap(), ); instruction.erase_from_basic_block(); } @@ -101,18 +91,14 @@ fn operation(instruction: InstructionValue<'_>, target: CodegenTarget) -> Option if !wasm && matches!(instruction.get_opcode(), UDiv | SDiv | URem | SRem) { // LLVM's native DAG lowering expands power-of-two divisors into // shifts/masks (with signed rounding adjustments), even at O0. - if let Some(divisor) = instruction - .get_operand(1) - .and_then(|v| v.value()) - .filter(|v| v.is_int_value()) + if let Some(divisor) = + instruction.get_operand(1).and_then(|v| v.value()).filter(|v| v.is_int_value()) { let divisor = divisor.into_int_value(); if divisor.is_const() { let text = divisor.print_to_string().to_string(); - if let Some(value) = text - .split_whitespace() - .last() - .and_then(|s| s.parse::().ok()) + if let Some(value) = + text.split_whitespace().last().and_then(|s| s.parse::().ok()) { let magnitude = if matches!(instruction.get_opcode(), SDiv | SRem) { value.unsigned_abs() @@ -126,12 +112,7 @@ fn operation(instruction: InstructionValue<'_>, target: CodegenTarget) -> Option } } } - let source = instruction - .get_operand(0)? - .value()? - .get_type() - .print_to_string() - .to_string(); + let source = instruction.get_operand(0)?.value()?.get_type().print_to_string().to_string(); let target = instruction.get_type().print_to_string().to_string(); let opcode = match instruction.get_opcode() { UDiv if source == "i128" => "udiv", @@ -141,11 +122,7 @@ fn operation(instruction: InstructionValue<'_>, target: CodegenTarget) -> Option Mul if source == "i128" => "mul", Shl | LShr | AShr if source == "i128" - && !instruction - .get_operand(1)? - .value()? - .into_int_value() - .is_const() => + && !instruction.get_operand(1)?.value()?.into_int_value().is_const() => { match instruction.get_opcode() { Shl => "shl", @@ -153,7 +130,7 @@ fn operation(instruction: InstructionValue<'_>, target: CodegenTarget) -> Option AShr => "ashr", _ => unreachable!(), } - } + }, UIToFP if source == "i128" && matches!(target.as_str(), "float" | "double") => "uitofp", SIToFP if source == "i128" && matches!(target.as_str(), "float" | "double") => "sitofp", FPToUI if target == "i128" && matches!(source.as_str(), "float" | "double") => "fptoui", diff --git a/llvm/src/codegen/arithmetic_runtime/templates.rs b/llvm/src/codegen/arithmetic_runtime/templates.rs index 4053f196..ba983c10 100644 --- a/llvm/src/codegen/arithmetic_runtime/templates.rs +++ b/llvm/src/codegen/arithmetic_runtime/templates.rs @@ -94,11 +94,8 @@ fn multiply(name: &str) -> String { } fn to_float(name: &str, signed: bool, float: &str) -> String { - let (precision, repr, fraction, bias) = if float == "float" { - (24, "i32", 23, 127) - } else { - (53, "i64", 52, 1023) - }; + let (precision, repr, fraction, bias) = + if float == "float" { (24, "i32", 23, 127) } else { (53, "i64", 52, 1023) }; let signs = if signed { " %negative = icmp slt i128 %a, 0\n %negated = sub i128 0, %a\n %magnitude = select i1 %negative, i128 %negated, i128 %a\n" } else { @@ -154,11 +151,8 @@ round: } fn from_float(name: &str, signed: bool, float: &str) -> String { - let (repr, fraction, bias, total) = if float == "float" { - ("i32", 23, 127, 32) - } else { - ("i64", 52, 1023, 64) - }; + let (repr, fraction, bias, total) = + if float == "float" { ("i32", 23, 127, 32) } else { ("i64", 52, 1023, 64) }; let mask = (1u64 << fraction) - 1; let hidden = 1u64 << fraction; let expmask = if float == "float" { 255 } else { 2047 }; @@ -242,11 +236,7 @@ fn shift(name: &str, op: &str) -> String { let large = if op == "shl" { " %low_large = or i64 0, 0\n %high_large = shl i64 %low, %extra\n".to_string() } else { - let fill = if op == "ashr" { - "ashr i64 %high, 63" - } else { - "or i64 0, 0" - }; + let fill = if op == "ashr" { "ashr i64 %high, 63" } else { "or i64 0, 0" }; format!(" %low_large = {right} i64 %high, %extra\n %high_large = {fill}\n") }; format!( diff --git a/llvm/src/codegen/consts.rs b/llvm/src/codegen/consts.rs index a48e16b8..bb5b5d86 100644 --- a/llvm/src/codegen/consts.rs +++ b/llvm/src/codegen/consts.rs @@ -34,11 +34,7 @@ use super::types::{wave_type_to_llvm_type, TypeFlavor}; #[derive(Debug, Clone)] pub enum ConstEvalError { UnknownIdentifier(String), - TypeMismatch { - expected: String, - got: String, - note: String, - }, + TypeMismatch { expected: String, got: String, note: String }, InvalidLiteral(String), Unsupported(String), } @@ -47,17 +43,9 @@ impl fmt::Display for ConstEvalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ConstEvalError::UnknownIdentifier(n) => write!(f, "unknown const identifier `{}`", n), - ConstEvalError::TypeMismatch { - expected, - got, - note, - } => { - write!( - f, - "type mismatch (expected {}, got {}): {}", - expected, got, note - ) - } + ConstEvalError::TypeMismatch { expected, got, note } => { + write!(f, "type mismatch (expected {}, got {}): {}", expected, got, note) + }, ConstEvalError::InvalidLiteral(s) => write!(f, "invalid literal: {}", s), ConstEvalError::Unsupported(s) => write!(f, "unsupported const expression: {}", s), } @@ -101,15 +89,15 @@ fn const_from_expected<'ctx>( return Ok(ty .const_int_arbitrary_precision(&n.to_le_words(ty.get_bit_width() as u16)) .into()); - } + }, (hir::ConstantValue::Float(n), BasicTypeEnum::FloatType(ty)) => { return Ok(ty.const_float(*n).into()); - } + }, _ => { return Err(ConstEvalError::Unsupported( "ICE: folded constant differs from HIR storage type".into(), )) - } + }, } } let Some(fact) = program.numeric_expression_of(expr) else { @@ -125,12 +113,8 @@ fn const_from_expected<'ctx>( target_data, ); }; - let native = wave_type_to_llvm_type( - context, - &fact.evaluation_type, - struct_types, - TypeFlavor::Value, - ); + let native = + wave_type_to_llvm_type(context, &fact.evaluation_type, struct_types, TypeFlavor::Value); let mut value = match expr { Expression::Cast { expr: inner, .. } | Expression::Grouped(inner) => const_from_expected( context, @@ -187,9 +171,7 @@ fn const_from_expected<'ctx>( } } if value.as_instruction_value().is_some() { - return Err(ConstEvalError::Unsupported( - "conversion did not fold to a constant".into(), - )); + return Err(ConstEvalError::Unsupported("conversion did not fold to a constant".into())); } Ok(value) } @@ -222,7 +204,7 @@ fn const_raw<'ctx>( note: "variant constructor used where a non-variant constant is expected" .to_string(), }); - } + }, }; let args: &[Expression] = match expr { Expression::FunctionCall { args, .. } => args, @@ -236,7 +218,7 @@ fn const_raw<'ctx>( }, construction.case_name ))); - } + }, }; if args.len() != construction.payload_types.len() { return Err(ConstEvalError::Unsupported(format!( @@ -259,14 +241,12 @@ fn const_raw<'ctx>( let mut payload_values = Vec::with_capacity(args.len()); for (index, argument) in args.iter().enumerate() { - let field_type = payload_ty - .get_field_type_at_index(index as u32) - .ok_or_else(|| { - ConstEvalError::Unsupported(format!( - "variant case '{}' has no payload field {}", - construction.case_name, index - )) - })?; + let field_type = payload_ty.get_field_type_at_index(index as u32).ok_or_else(|| { + ConstEvalError::Unsupported(format!( + "variant case '{}' has no payload field {}", + construction.case_name, index + )) + })?; payload_values.push(const_from_expected( context, module, @@ -293,10 +273,7 @@ fn const_raw<'ctx>( .const_int(construction.discriminant as u64, false) .as_basic_value_enum(); let payload_value = payload_ty.const_named_struct(&payload_values); - let storage_ty = variant_ty - .get_field_type_at_index(2) - .unwrap() - .into_array_type(); + let storage_ty = variant_ty.get_field_type_at_index(2).unwrap().into_array_type(); let mut bytes = vec![0; storage_ty.len() as usize]; super::variants::constant_storage_bytes( context, @@ -321,7 +298,7 @@ fn const_raw<'ctx>( program, target_data, ); - } + }, Expression::Variable(name) => { let v = match const_env.get(name) { @@ -346,7 +323,7 @@ fn const_raw<'ctx>( } Ok(v) - } + }, Expression::Null => match expected { BasicTypeEnum::PointerType(pt) => Ok(pt.const_null().as_basic_value_enum()), @@ -357,9 +334,9 @@ fn const_raw<'ctx>( }), }, - Expression::Cast { .. } => Err(ConstEvalError::Unsupported( - "ICE: cast missing HIR conversion facts".into(), - )), + Expression::Cast { .. } => { + Err(ConstEvalError::Unsupported("ICE: cast missing HIR conversion facts".into())) + }, Expression::Literal(Literal::String(bytes)) => { let value = context.const_string(bytes, true); let global = module.add_global(value.get_type(), None, "$const$str"); @@ -367,17 +344,17 @@ fn const_raw<'ctx>( global.set_constant(true); global.set_linkage(Linkage::Private); Ok(global.as_pointer_value().into()) - } + }, Expression::Literal(Literal::Char(value)) => { Ok(context.i8_type().const_int(*value as u64, false).into()) - } + }, Expression::Literal(Literal::Byte(value)) => { Ok(context.i8_type().const_int(u64::from(*value), false).into()) - } + }, Expression::Literal(Literal::Bool(value)) => match expected { BasicTypeEnum::IntType(ty) => { Ok(ty.const_int(u64::from(*value), false).as_basic_value_enum()) - } + }, _ => Err(ConstEvalError::TypeMismatch { expected: type_name(expected), got: "bool".into(), @@ -387,15 +364,11 @@ fn const_raw<'ctx>( // --- ints --- Expression::Literal(Literal::Int(s)) => match expected { BasicTypeEnum::FloatType(float_ty) => { - let bits = if float_ty == context.f32_type() { - 32 - } else { - 64 - }; + let bits = if float_ty == context.f32_type() { 32 } else { 64 }; let value = hir::integer_literal_float(s, bits) .ok_or_else(|| ConstEvalError::InvalidLiteral(s.clone()))?; Ok(float_ty.const_float(value).as_basic_value_enum()) - } + }, BasicTypeEnum::IntType(int_ty) => { let (neg, radix, digits) = parse_signed_and_radix(s); let mut iv = int_ty @@ -406,7 +379,7 @@ fn const_raw<'ctx>( iv = iv.const_neg(); } Ok(iv.as_basic_value_enum()) - } + }, BasicTypeEnum::PointerType(ptr_ty) => { if is_zero_like(s) { Ok(ptr_ty.const_null().as_basic_value_enum()) @@ -417,7 +390,7 @@ fn const_raw<'ctx>( note: "only 0 can be used as a const null pointer literal".to_string(), }) } - } + }, _ => Err(ConstEvalError::TypeMismatch { expected: type_name(expected), got: format!("int({})", s), @@ -429,7 +402,7 @@ fn const_raw<'ctx>( Expression::Literal(Literal::Float(fv)) => match expected { BasicTypeEnum::FloatType(float_ty) => { Ok(float_ty.const_float(*fv).as_basic_value_enum()) - } + }, _ => Err(ConstEvalError::TypeMismatch { expected: type_name(expected), got: "float".to_string(), @@ -447,7 +420,7 @@ fn const_raw<'ctx>( got: "struct-literal".to_string(), note: format!("StructLiteral '{}' used where non-struct expected", name), }) - } + }, }; let field_count = st.count_fields() as usize; @@ -455,10 +428,7 @@ fn const_raw<'ctx>( let struct_name = if !name.is_empty() { name.as_str() } else { - st.get_name() - .and_then(|c| c.to_str().ok()) - .map(strip_struct_prefix) - .unwrap_or("") + st.get_name().and_then(|c| c.to_str().ok()).map(strip_struct_prefix).unwrap_or("") }; let positional = fields.iter().all(|(n, _)| n.is_empty()); @@ -540,7 +510,7 @@ fn const_raw<'ctx>( } Ok(st.const_named_struct(&ordered).as_basic_value_enum()) - } + }, // --- array literal --- Expression::ArrayLiteral(elems) => match expected { @@ -585,11 +555,11 @@ fn const_raw<'ctx>( got: value_type_name(other), note: "array element expected int".to_string(), }) - } + }, } } Ok(int_ty.const_array(&vs).as_basic_value_enum()) - } + }, BasicTypeEnum::FloatType(float_ty) => { let mut vs = Vec::with_capacity(len); @@ -602,11 +572,11 @@ fn const_raw<'ctx>( got: value_type_name(other), note: "array element expected float".to_string(), }) - } + }, } } Ok(float_ty.const_array(&vs).as_basic_value_enum()) - } + }, BasicTypeEnum::PointerType(ptr_ty) => { let mut vs = Vec::with_capacity(len); @@ -619,11 +589,11 @@ fn const_raw<'ctx>( got: value_type_name(other), note: "array element expected pointer".to_string(), }) - } + }, } } Ok(ptr_ty.const_array(&vs).as_basic_value_enum()) - } + }, BasicTypeEnum::StructType(st_ty) => { let mut vs = Vec::with_capacity(len); @@ -636,11 +606,11 @@ fn const_raw<'ctx>( got: value_type_name(other), note: "array element expected struct".to_string(), }) - } + }, } } Ok(st_ty.const_array(&vs).as_basic_value_enum()) - } + }, BasicTypeEnum::ArrayType(inner_at) => { let mut vs = Vec::with_capacity(len); @@ -653,18 +623,18 @@ fn const_raw<'ctx>( got: value_type_name(other), note: "array element expected array".to_string(), }) - } + }, } } Ok(inner_at.const_array(&vs).as_basic_value_enum()) - } + }, other => Err(ConstEvalError::Unsupported(format!( "Unsupported const array element type: {:?}", other ))), } - } + }, _ => Err(ConstEvalError::TypeMismatch { expected: type_name(expected), got: "array-literal".to_string(), diff --git a/llvm/src/codegen/conversions.rs b/llvm/src/codegen/conversions.rs index 222f41d2..a426f7ea 100644 --- a/llvm/src/codegen/conversions.rs +++ b/llvm/src/codegen/conversions.rs @@ -36,30 +36,18 @@ pub(crate) fn apply<'ctx>( use ConversionKind::*; let source = wave_type_to_llvm_type(context, &info.source_type, structs, TypeFlavor::Value); let target = wave_type_to_llvm_type(context, &info.target_type, structs, TypeFlavor::Value); - assert_eq!( - value.get_type(), - source, - "ICE: conversion source differs from verified HIR" - ); + assert_eq!(value.get_type(), source, "ICE: conversion source differs from verified HIR"); match info.kind { Identity | ReinterpretInteger => { assert_eq!(source, target); value - } + }, SignExtend => builder - .build_int_s_extend( - value.into_int_value(), - target.into_int_type(), - "convert.sext", - ) + .build_int_s_extend(value.into_int_value(), target.into_int_type(), "convert.sext") .unwrap() .into(), ZeroExtend => builder - .build_int_z_extend( - value.into_int_value(), - target.into_int_type(), - "convert.zext", - ) + .build_int_z_extend(value.into_int_value(), target.into_int_type(), "convert.zext") .unwrap() .into(), IntegerToBool => builder @@ -81,11 +69,7 @@ pub(crate) fn apply<'ctx>( .unwrap() .into(), Truncate => builder - .build_int_truncate( - value.into_int_value(), - target.into_int_type(), - "convert.trunc", - ) + .build_int_truncate(value.into_int_value(), target.into_int_type(), "convert.trunc") .unwrap() .into(), SignedToFloat => builder @@ -112,11 +96,7 @@ pub(crate) fn apply<'ctx>( let range = hir::conversions::float_integer_range(&info.source_type, &info.target_type); let low = builder .build_float_compare( - if range.lower_inclusive { - FloatPredicate::OGE - } else { - FloatPredicate::OGT - }, + if range.lower_inclusive { FloatPredicate::OGE } else { FloatPredicate::OGT }, input, ty.const_float(range.lower), "convert.lower", @@ -140,9 +120,7 @@ pub(crate) fn apply<'ctx>( ) .unwrap(); let valid = builder.build_and(low, high, "convert.range").unwrap(); - let valid = builder - .build_and(valid, finite_low, "convert.valid") - .unwrap(); + let valid = builder.build_and(valid, finite_low, "convert.valid").unwrap(); trap_unless(context, builder, module, valid); if bits > 64 && input.get_constant().is_none() { return wide_float_to_int(context, builder, input, target.into_int_type()); @@ -158,29 +136,17 @@ pub(crate) fn apply<'ctx>( .unwrap() .into() } - } + }, FloatExtend | FloatTruncate => builder - .build_float_cast( - value.into_float_value(), - target.into_float_type(), - "convert.float", - ) + .build_float_cast(value.into_float_value(), target.into_float_type(), "convert.float") .unwrap() .into(), PointerToInteger => builder - .build_ptr_to_int( - value.into_pointer_value(), - target.into_int_type(), - "convert.ptrint", - ) + .build_ptr_to_int(value.into_pointer_value(), target.into_int_type(), "convert.ptrint") .unwrap() .into(), IntegerToPointer => builder - .build_int_to_ptr( - value.into_int_value(), - target.into_pointer_type(), - "convert.intptr", - ) + .build_int_to_ptr(value.into_int_value(), target.into_pointer_type(), "convert.intptr") .unwrap() .into(), PointerCast => builder @@ -220,15 +186,12 @@ pub(crate) fn binary<'ctx>( ) .unwrap(); trap_unless(context, builder, module, valid); - let count = builder - .build_int_cast(count, lhs.get_type(), "shift.narrow") - .unwrap(); + let count = builder.build_int_cast(count, lhs.get_type(), "shift.narrow").unwrap(); return match operator { Operator::ShiftLeft => builder.build_left_shift(lhs, count, "shl").unwrap().into(), - Operator::ShiftRight => builder - .build_right_shift(lhs, count, !unsigned(computation), "shr") - .unwrap() - .into(), + Operator::ShiftRight => { + builder.build_right_shift(lhs, count, !unsigned(computation), "shr").unwrap().into() + }, _ => panic!("ICE: shift facts on a non-shift operation"), }; } @@ -246,59 +209,47 @@ pub(crate) fn binary<'ctx>( Operator::Multiply => builder.build_int_mul(l_casted, r_casted, "multmp"), Operator::Divide if operation_unsigned => { builder.build_int_unsigned_div(l_casted, r_casted, "divtmp") - } + }, Operator::Divide => builder.build_int_signed_div(l_casted, r_casted, "divtmp"), Operator::Remainder if operation_unsigned => { builder.build_int_unsigned_rem(l_casted, r_casted, "modtmp") - } + }, Operator::Remainder => builder.build_int_signed_rem(l_casted, r_casted, "modtmp"), Operator::ShiftLeft => builder.build_left_shift(l_casted, r_casted, "shl"), Operator::ShiftRight => { let arithmetic = !operation_unsigned; builder.build_right_shift(l_casted, r_casted, arithmetic, "shr") - } + }, Operator::BitwiseAnd => builder.build_and(l_casted, r_casted, "andtmp"), Operator::BitwiseOr => builder.build_or(l_casted, r_casted, "ortmp"), Operator::BitwiseXor => builder.build_xor(l_casted, r_casted, "xortmp"), Operator::Greater => { - let predicate = if operation_unsigned { - IntPredicate::UGT - } else { - IntPredicate::SGT - }; + let predicate = + if operation_unsigned { IntPredicate::UGT } else { IntPredicate::SGT }; builder.build_int_compare(predicate, l_casted, r_casted, "cmptmp") - } + }, Operator::Less => { - let predicate = if operation_unsigned { - IntPredicate::ULT - } else { - IntPredicate::SLT - }; + let predicate = + if operation_unsigned { IntPredicate::ULT } else { IntPredicate::SLT }; builder.build_int_compare(predicate, l_casted, r_casted, "cmptmp") - } + }, Operator::Equal => { builder.build_int_compare(IntPredicate::EQ, l_casted, r_casted, "cmptmp") - } + }, Operator::NotEqual => { builder.build_int_compare(IntPredicate::NE, l_casted, r_casted, "cmptmp") - } + }, Operator::GreaterEqual => { - let predicate = if operation_unsigned { - IntPredicate::UGE - } else { - IntPredicate::SGE - }; + let predicate = + if operation_unsigned { IntPredicate::UGE } else { IntPredicate::SGE }; builder.build_int_compare(predicate, l_casted, r_casted, "cmptmp") - } + }, Operator::LessEqual => { - let predicate = if operation_unsigned { - IntPredicate::ULE - } else { - IntPredicate::SLE - }; + let predicate = + if operation_unsigned { IntPredicate::ULE } else { IntPredicate::SLE }; builder.build_int_compare(predicate, l_casted, r_casted, "cmptmp") - } + }, Operator::LogicalAnd | Operator::LogicalOr => unreachable!(), @@ -307,29 +258,24 @@ pub(crate) fn binary<'ctx>( .unwrap(); result.into() - } + }, (BasicValueEnum::FloatValue(l), BasicValueEnum::FloatValue(r)) => { let result: BasicValueEnum<'ctx> = match operator { - Operator::Add => builder - .build_float_add(l, r, "faddtmp") - .unwrap() - .as_basic_value_enum(), - Operator::Subtract => builder - .build_float_sub(l, r, "fsubtmp") - .unwrap() - .as_basic_value_enum(), - Operator::Multiply => builder - .build_float_mul(l, r, "fmultmp") - .unwrap() - .as_basic_value_enum(), - Operator::Divide => builder - .build_float_div(l, r, "fdivtmp") - .unwrap() - .as_basic_value_enum(), - Operator::Remainder => builder - .build_float_rem(l, r, "fmodtmp") - .unwrap() - .as_basic_value_enum(), + Operator::Add => { + builder.build_float_add(l, r, "faddtmp").unwrap().as_basic_value_enum() + }, + Operator::Subtract => { + builder.build_float_sub(l, r, "fsubtmp").unwrap().as_basic_value_enum() + }, + Operator::Multiply => { + builder.build_float_mul(l, r, "fmultmp").unwrap().as_basic_value_enum() + }, + Operator::Divide => { + builder.build_float_div(l, r, "fdivtmp").unwrap().as_basic_value_enum() + }, + Operator::Remainder => { + builder.build_float_rem(l, r, "fmodtmp").unwrap().as_basic_value_enum() + }, Operator::Greater => builder .build_float_compare(FloatPredicate::OGT, l, r, "fcmpgt") @@ -360,7 +306,7 @@ pub(crate) fn binary<'ctx>( }; result - } + }, _ => panic!("ICE: nonnumeric computation"), } } @@ -381,10 +327,7 @@ fn trap_unless<'ctx>( let bad = context.append_basic_block(parent, "numeric.invalid"); builder.build_conditional_branch(valid, ok, bad).unwrap(); builder.position_at_end(bad); - let trap = Intrinsic::find("llvm.trap") - .unwrap() - .get_declaration(module, &[]) - .unwrap(); + let trap = Intrinsic::find("llvm.trap").unwrap().get_declaration(module, &[]).unwrap(); builder.build_call(trap, &[], "").unwrap(); builder.build_unreachable().unwrap(); builder.position_at_end(ok); @@ -399,53 +342,24 @@ fn wide_float_to_int<'ctx>( target: inkwell::types::IntType<'ctx>, ) -> BasicValueEnum<'ctx> { let single = value.get_type() == context.f32_type(); - let (bits, fraction, bias, exponent_mask) = if single { - (32, 23, 127, 255) - } else { - (64, 52, 1023, 2047) - }; + let (bits, fraction, bias, exponent_mask) = + if single { (32, 23, 127, 255) } else { (64, 52, 1023, 2047) }; let storage = context.custom_width_int_type(bits); - let raw = builder - .build_bit_cast(value, storage, "convert.ieee") - .unwrap() - .into_int_value(); + let raw = builder.build_bit_cast(value, storage, "convert.ieee").unwrap().into_int_value(); let negative = builder - .build_int_compare( - IntPredicate::SLT, - raw, - storage.const_zero(), - "convert.negative", - ) + .build_int_compare(IntPredicate::SLT, raw, storage.const_zero(), "convert.negative") .unwrap(); let exponent = builder - .build_right_shift( - raw, - storage.const_int(fraction, false), - false, - "convert.exponent", - ) + .build_right_shift(raw, storage.const_int(fraction, false), false, "convert.exponent") .unwrap(); let exponent = builder - .build_and( - exponent, - storage.const_int(exponent_mask, false), - "convert.exponent.bits", - ) + .build_and(exponent, storage.const_int(exponent_mask, false), "convert.exponent.bits") .unwrap(); let shift = builder - .build_int_sub( - exponent, - storage.const_int(bias + fraction, false), - "convert.shift", - ) + .build_int_sub(exponent, storage.const_int(bias + fraction, false), "convert.shift") .unwrap(); let leftward = builder - .build_int_compare( - IntPredicate::SGE, - shift, - storage.const_zero(), - "convert.leftward", - ) + .build_int_compare(IntPredicate::SGE, shift, storage.const_zero(), "convert.leftward") .unwrap(); let left_count = builder .build_select(leftward, shift, storage.const_zero(), "convert.left.count") @@ -453,12 +367,7 @@ fn wide_float_to_int<'ctx>( .into_int_value(); let neg_shift = builder.build_int_neg(shift, "convert.right.shift").unwrap(); let right_count = builder - .build_select( - leftward, - storage.const_zero(), - neg_shift, - "convert.right.count", - ) + .build_select(leftward, storage.const_zero(), neg_shift, "convert.right.count") .unwrap() .into_int_value(); let too_large = builder @@ -479,33 +388,15 @@ fn wide_float_to_int<'ctx>( .unwrap() .into_int_value(); let significand = builder - .build_and( - raw, - storage.const_int((1u64 << fraction) - 1, false), - "convert.fraction", - ) + .build_and(raw, storage.const_int((1u64 << fraction) - 1, false), "convert.fraction") .unwrap(); let significand = builder - .build_or( - significand, - storage.const_int(1u64 << fraction, false), - "convert.significand", - ) - .unwrap(); - let low = builder - .build_right_shift(significand, right_count, false, "convert.low") - .unwrap(); - let magnitude = builder - .build_int_z_extend(low, target, "convert.wide") - .unwrap(); - let left_count = builder - .build_int_z_extend(left_count, target, "convert.wide.count") - .unwrap(); - let magnitude = builder - .build_left_shift(magnitude, left_count, "convert.magnitude") + .build_or(significand, storage.const_int(1u64 << fraction, false), "convert.significand") .unwrap(); + let low = builder.build_right_shift(significand, right_count, false, "convert.low").unwrap(); + let magnitude = builder.build_int_z_extend(low, target, "convert.wide").unwrap(); + let left_count = builder.build_int_z_extend(left_count, target, "convert.wide.count").unwrap(); + let magnitude = builder.build_left_shift(magnitude, left_count, "convert.magnitude").unwrap(); let negated = builder.build_int_neg(magnitude, "convert.negate").unwrap(); - builder - .build_select(negative, negated, magnitude, "convert.integer") - .unwrap() + builder.build_select(negative, negated, magnitude, "convert.integer").unwrap() } diff --git a/llvm/src/codegen/format.rs b/llvm/src/codegen/format.rs index 59743712..c78ce0bd 100644 --- a/llvm/src/codegen/format.rs +++ b/llvm/src/codegen/format.rs @@ -37,7 +37,7 @@ pub fn wave_format_to_c(format: &[u8], arg_formats: &[&str]) -> Vec { FormatFragment::Literal(bytes) => result.extend(escape_percent(bytes)), FormatFragment::Placeholder(_) => { result.extend_from_slice(formats.next().expect("validated arity").as_bytes()) - } + }, } } assert!(formats.next().is_none(), "validated format arity"); diff --git a/llvm/src/codegen/ir.rs b/llvm/src/codegen/ir.rs index 71a5ee5c..f0eed73d 100644 --- a/llvm/src/codegen/ir.rs +++ b/llvm/src/codegen/ir.rs @@ -91,14 +91,10 @@ fn reinterpret_abi_value<'ctx>( let source = value.get_type(); let source_size = td.get_store_size(&source); let target_size = td.get_store_size(&target); - let source_is_aggregate = matches!( - source, - BasicTypeEnum::ArrayType(_) | BasicTypeEnum::StructType(_) - ); - let target_is_aggregate = matches!( - target, - BasicTypeEnum::ArrayType(_) | BasicTypeEnum::StructType(_) - ); + let source_is_aggregate = + matches!(source, BasicTypeEnum::ArrayType(_) | BasicTypeEnum::StructType(_)); + let target_is_aggregate = + matches!(target, BasicTypeEnum::ArrayType(_) | BasicTypeEnum::StructType(_)); if source_size != target_size && !(source_is_aggregate || target_is_aggregate) { panic!( "cannot reinterpret C ABI value '{}' from {} bytes to {} bytes", @@ -106,19 +102,11 @@ fn reinterpret_abi_value<'ctx>( ); } - let source_ptr = builder - .build_alloca(source, &format!("{}_source", tag)) - .unwrap(); + let source_ptr = builder.build_alloca(source, &format!("{}_source", tag)).unwrap(); builder.build_store(source_ptr, value).unwrap(); - let target_ptr = builder - .build_alloca(target, &format!("{}_target", tag)) - .unwrap(); - builder - .build_store(target_ptr, target.const_zero()) - .unwrap(); - let size = context - .i64_type() - .const_int(source_size.min(target_size), false); + let target_ptr = builder.build_alloca(target, &format!("{}_target", tag)).unwrap(); + builder.build_store(target_ptr, target.const_zero()).unwrap(); + let size = context.i64_type().const_int(source_size.min(target_size), false); builder .build_memcpy( target_ptr, @@ -128,10 +116,7 @@ fn reinterpret_abi_value<'ctx>( size, ) .unwrap(); - builder - .build_load(target, target_ptr, &format!("{}_load", tag)) - .unwrap() - .as_basic_value_enum() + builder.build_load(target, target_ptr, &format!("{}_load", tag)).unwrap().as_basic_value_enum() } fn rebuild_split_abi_value<'ctx>( @@ -142,21 +127,15 @@ fn rebuild_split_abi_value<'ctx>( target: BasicTypeEnum<'ctx>, tag: &str, ) -> BasicValueEnum<'ctx> { - let target_ptr = builder - .build_alloca(target, &format!("{}_target", tag)) - .unwrap(); + let target_ptr = builder.build_alloca(target, &format!("{}_target", tag)).unwrap(); let target_size = td.get_store_size(&target); - builder - .build_store(target_ptr, target.const_zero()) - .unwrap(); + builder.build_store(target_ptr, target.const_zero()).unwrap(); let mut offset = 0u64; for (index, part) in parts.iter().enumerate() { let part_type = part.get_type(); let part_size = td.get_store_size(&part_type); - let part_ptr = builder - .build_alloca(part_type, &format!("{}_part_{}", tag, index)) - .unwrap(); + let part_ptr = builder.build_alloca(part_type, &format!("{}_part_{}", tag, index)).unwrap(); builder.build_store(part_ptr, *part).unwrap(); let offset_value = context.i64_type().const_int(offset, false); // SAFETY: `target_ptr` is an opaque pointer to a live stack allocation; @@ -187,10 +166,7 @@ fn rebuild_split_abi_value<'ctx>( } offset += part_size; } - builder - .build_load(target, target_ptr, &format!("{}_load", tag)) - .unwrap() - .as_basic_value_enum() + builder.build_load(target, target_ptr, &format!("{}_load", tag)).unwrap().as_basic_value_enum() } fn rebuild_expanded_abi_value<'ctx>( @@ -204,12 +180,8 @@ fn rebuild_expanded_abi_value<'ctx>( ) -> BasicValueEnum<'ctx> { assert_eq!(parts.len(), values.len()); let target_size = td.get_store_size(&target); - let target_ptr = builder - .build_alloca(target, &format!("{tag}_target")) - .unwrap(); - builder - .build_store(target_ptr, target.const_zero()) - .unwrap(); + let target_ptr = builder.build_alloca(target, &format!("{tag}_target")).unwrap(); + builder.build_store(target_ptr, target.const_zero()).unwrap(); for (index, (part, value)) in parts.iter().zip(values).enumerate() { let part_size = td.get_store_size(&part.ty); @@ -217,9 +189,7 @@ fn rebuild_expanded_abi_value<'ctx>( part.offset + part_size <= target_size, "LoongArch ABI part extends past its aggregate" ); - let part_ptr = builder - .build_alloca(part.ty, &format!("{tag}_part_{index}")) - .unwrap(); + let part_ptr = builder.build_alloca(part.ty, &format!("{tag}_part_{index}")).unwrap(); builder.build_store(part_ptr, *value).unwrap(); let offset = context.i64_type().const_int(part.offset, false); // SAFETY: the classifier obtained this byte offset from TargetData and @@ -245,10 +215,7 @@ fn rebuild_expanded_abi_value<'ctx>( .unwrap(); } - builder - .build_load(target, target_ptr, &format!("{tag}_load")) - .unwrap() - .as_basic_value_enum() + builder.build_load(target, target_ptr, &format!("{tag}_load")).unwrap().as_basic_value_enum() } fn build_export_c_wrapper<'ctx>( @@ -275,12 +242,8 @@ fn build_export_c_wrapper<'ctx>( }; let mut implementation_args = Vec::>::new(); - for (wave_index, (lowering, wave_type)) in export - .info - .params - .iter() - .zip(export.wave_param_types.iter()) - .enumerate() + for (wave_index, (lowering, wave_type)) in + export.info.params.iter().zip(export.wave_param_types.iter()).enumerate() { let value = match lowering { ParamLowering::Ignore => wave_type.const_zero(), @@ -298,7 +261,7 @@ fn build_export_c_wrapper<'ctx>( *wave_type, &format!("export_arg_{}", wave_index), ) - } + }, ParamLowering::Indirect { .. } | ParamLowering::ByVal { .. } => { let pointer = export .wrapper @@ -310,7 +273,7 @@ fn build_export_c_wrapper<'ctx>( .build_load(*wave_type, pointer, &format!("export_byval_{}", wave_index)) .unwrap() .as_basic_value_enum() - } + }, ParamLowering::Split(parts) => { let mut incoming = Vec::with_capacity(parts.len()); for _ in parts { @@ -330,7 +293,7 @@ fn build_export_c_wrapper<'ctx>( *wave_type, &format!("export_split_{}", wave_index), ) - } + }, ParamLowering::CoerceAndExpand(parts) => { let mut incoming = Vec::with_capacity(parts.len()); for _ in parts { @@ -351,52 +314,45 @@ fn build_export_c_wrapper<'ctx>( *wave_type, &format!("export_loong_expand_{}", wave_index), ) - } + }, }; implementation_args.push(value.into()); } let call = builder - .build_call( - export.implementation, - &implementation_args, - "export_implementation", - ) + .build_call(export.implementation, &implementation_args, "export_implementation") .unwrap(); match &export.info.ret { RetLowering::Void => { builder.build_return(None).unwrap(); - } + }, RetLowering::SRet { .. } => { let value = match call.try_as_basic_value() { ValueKind::Basic(value) => value, ValueKind::Instruction(_) => { panic!("C ABI sret wrapper implementation returned void") - } + }, }; - builder - .build_store(sret_ptr.expect("missing C ABI sret pointer"), value) - .unwrap(); + builder.build_store(sret_ptr.expect("missing C ABI sret pointer"), value).unwrap(); builder.build_return(None).unwrap(); - } + }, RetLowering::Direct(lowered_type) => { let value = match call.try_as_basic_value() { ValueKind::Basic(value) => value, ValueKind::Instruction(_) => { panic!("C ABI direct wrapper implementation returned void") - } + }, }; - let wave_type = export - .wave_ret_type - .expect("direct C ABI wrapper requires a Wave return type"); + let wave_type = + export.wave_ret_type.expect("direct C ABI wrapper requires a Wave return type"); if value.get_type() != wave_type { panic!("C ABI wrapper implementation return type changed unexpectedly"); } let lowered = reinterpret_abi_value(context, builder, td, value, *lowered_type, "export_return"); builder.build_return(Some(&lowered)).unwrap(); - } + }, } } @@ -408,7 +364,7 @@ fn is_supported_extern_abi(abi: &str, target: CodegenTarget) -> bool { match target { CodegenTarget::WindowsX86_64Msvc | CodegenTarget::WindowsArm64Msvc => { abi.eq_ignore_ascii_case("c") || abi.eq_ignore_ascii_case("system") - } + }, _ => abi.eq_ignore_ascii_case("c"), } } @@ -434,13 +390,7 @@ fn target_opt_level_from_flag(opt_flag: &str) -> Result OptimizationLevel::Less, "-O2" | "-Os" | "-Oz" => OptimizationLevel::Default, "-O3" => OptimizationLevel::Aggressive, - other => { - return Err(CodegenError::new( - CodegenPhase::Target, - "optimization level", - other, - )) - } + other => return Err(CodegenError::new(CodegenPhase::Target, "optimization level", other)), }) } @@ -477,12 +427,8 @@ fn reloc_mode_from_backend( "pic" | "pie" => RelocMode::PIC, "dynamic-no-pic" | "dynamic_no_pic" => RelocMode::DynamicNoPic, other => { - return Err(CodegenError::new( - CodegenPhase::Target, - "relocation-model", - other, - )) - } + return Err(CodegenError::new(CodegenPhase::Target, "relocation-model", other)) + }, }); } @@ -673,9 +619,7 @@ fn build_wasi_start_wrapper<'ctx>( )); } - let exit_type = context - .void_type() - .fn_type(&[context.i32_type().into()], false); + let exit_type = context.void_type().fn_type(&[context.i32_type().into()], false); let proc_exit = module.add_function("__wasi_proc_exit", exit_type, None); apply_wasm_import_attrs(context, proc_exit, target, "proc_exit"); @@ -735,7 +679,7 @@ pub unsafe fn emit_codegen_file( output.display(), )); } - } + }, CodegenFileKind::Assembly | CodegenFileKind::Object => { codegen_trace("emit target machine output"); let file_type = if matches!(kind, CodegenFileKind::Assembly) { @@ -753,7 +697,7 @@ pub unsafe fn emit_codegen_file( e, ) })?; - } + }, } codegen_trace("commit output file"); pending.commit() @@ -777,37 +721,26 @@ fn build_module( .with_span(e.span) })?; lowered = hir::TypedProgram::lower(ast).map_err(|e| { - CodegenError::new( - CodegenPhase::Lowering, - "validate async state machine", - e.to_string(), - ) + CodegenError::new(CodegenPhase::Lowering, "validate async state machine", e.to_string()) })?; &lowered } else { program }; program.verify_conversions().map_err(|e| { - CodegenError::new( - CodegenPhase::Lowering, - "HIR conversion verifier (ICE)", - e.message, - ) - .with_span(e.span) + CodegenError::new(CodegenPhase::Lowering, "HIR conversion verifier (ICE)", e.message) + .with_span(e.span) })?; let ast_nodes = program.syntax(); let uses_tasks = program.uses_async_runtime(); for (symbol, span) in program.async_runtime_requirements() { let declaration = ast_nodes.iter().find_map(|node| match node { ASTNode::Function(function) - if function - .export - .as_ref() - .and_then(|export| export.symbol.as_deref()) + if function.export.as_ref().and_then(|export| export.symbol.as_deref()) == Some(symbol) => { Some(function) - } + }, _ => None, }); let Some(function) = declaration else { @@ -908,14 +841,9 @@ fn build_module( target_options = target_options.set_abi(abi); } } - let tm = target - .create_target_machine_from_options(&triple, target_options) - .ok_or_else(|| { - CodegenError::new( - CodegenPhase::Target, - "create target machine", - raw_target.as_ref(), - ) + let tm = + target.create_target_machine_from_options(&triple, target_options).ok_or_else(|| { + CodegenError::new(CodegenPhase::Target, "create target machine", raw_target.as_ref()) })?; codegen_trace("set target triple"); @@ -1039,17 +967,17 @@ fn build_module( Ok(val) => { global_consts.insert(v.name.clone(), val); progressed = true; - } + }, Err(ConstEvalError::UnknownIdentifier(_)) => { next_pending.push(v); - } + }, Err(e) => { return Err(CodegenError::new( CodegenPhase::Lowering, "lower program", format!("const '{}' evaluation failed: {}", v.name, e), )); - } + }, } } @@ -1102,10 +1030,7 @@ fn build_module( e, ) .with_span( - program - .expression_id(expr) - .and_then(|id| program.expression_span(id)) - .cloned(), + program.expression_id(expr).and_then(|id| program.expression_span(id)).cloned(), ) })? } else { @@ -1131,10 +1056,8 @@ fn build_module( let mut function_nodes = Vec::new(); for ast in ast_nodes { match ast { - ASTNode::Function(function) => function_nodes.push(FunctionCodegenEntry { - symbol: function.name.clone(), - node: function, - }), + ASTNode::Function(function) => function_nodes + .push(FunctionCodegenEntry { symbol: function.name.clone(), node: function }), ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { function_nodes.push(FunctionCodegenEntry { @@ -1142,7 +1065,7 @@ fn build_module( node: method, }); } - } + }, ASTNode::Struct(structure) if structure.generic_params.is_empty() => { for method in &structure.methods { function_nodes.push(FunctionCodegenEntry { @@ -1150,31 +1073,19 @@ fn build_module( node: method, }); } - } - _ => {} + }, + _ => {}, } } let extern_functions: Vec<&ExternFunctionNode> = ast_nodes .iter() - .filter_map(|ast| { - if let ASTNode::ExternFunction(ext) = ast { - Some(ext) - } else { - None - } - }) + .filter_map(|ast| if let ASTNode::ExternFunction(ext) = ast { Some(ext) } else { None }) .collect(); codegen_trace("declare functions"); for entry in &function_nodes { - let FunctionNode { - name, - parameters, - return_type, - export, - .. - } = entry.node; + let FunctionNode { name, parameters, return_type, export, .. } = entry.node; let symbol = &entry.symbol; if let Some(export) = export { if !is_supported_extern_abi(&export.abi, abi_target) { @@ -1206,7 +1117,7 @@ fn build_module( match return_type { None | Some(WaveType::Void | WaveType::Never) => { context.void_type().fn_type(¶m_types, false) - } + }, Some(wave_ret_ty) => { let llvm_ret_type = wave_type_to_llvm_type( context, @@ -1215,7 +1126,7 @@ fn build_module( TypeFlavor::AbiC, ); llvm_ret_type.fn_type(¶m_types, false) - } + }, } }; @@ -1343,14 +1254,8 @@ fn build_module( )); } - let lowered = lower_extern_c( - context, - td, - abi_target, - backend.abi.as_deref(), - ext, - &struct_types, - ); + let lowered = + lower_extern_c(context, td, abi_target, backend.abi.as_deref(), ext, &struct_types); let f = module.add_function(&lowered.llvm_name, lowered.fn_type, None); apply_extern_c_attrs(context, f, &lowered.info); @@ -1367,9 +1272,8 @@ fn build_module( if std::env::var_os("WAVE_CODEGEN_TRACE").is_some() { eprintln!("[wavec-codegen] lower function body: {}", entry.symbol); } - let function = *functions - .get(&entry.symbol) - .expect("validated function lowering invariant"); + let function = + *functions.get(&entry.symbol).expect("validated function lowering invariant"); let entry_block = context.append_basic_block(function, "entry"); builder.position_at_end(entry_block); @@ -1382,9 +1286,8 @@ fn build_module( let llvm_type = wave_type_to_llvm_type(context, ¶m.param_type, &struct_types, TypeFlavor::AbiC); let alloca = builder.build_alloca(llvm_type, ¶m.name)?; - let param_val = function - .get_nth_param(i as u32) - .expect("validated function lowering invariant"); + let param_val = + function.get_nth_param(i as u32).expect("validated function lowering invariant"); builder.build_store(alloca, param_val)?; variables.insert( @@ -1398,10 +1301,7 @@ fn build_module( } for stmt in &func_node.body { - if builder - .get_insert_block() - .is_some_and(|block| block.get_terminator().is_some()) - { + if builder.get_insert_block().is_some_and(|block| block.get_terminator().is_some()) { break; } if let ASTNode::Statement(_) | ASTNode::Variable(_) = stmt { @@ -1432,9 +1332,8 @@ fn build_module( } } - let current_block = builder - .get_insert_block() - .expect("validated function lowering invariant"); + let current_block = + builder.get_insert_block().expect("validated function lowering invariant"); if current_block.get_terminator().is_none() { let implicit_i32_main = is_implicit_i32_main(&func_node.name, &func_node.return_type); let is_void_like = match &func_node.return_type { @@ -1454,10 +1353,7 @@ fn build_module( return Err(CodegenError::new( CodegenPhase::Lowering, "lower program", - format!( - "Non-void function '{}' is missing a return statement", - func_node.name - ), + format!("Non-void function '{}' is missing a return statement", func_node.name), )); } } @@ -1490,10 +1386,7 @@ fn build_module( .map_err(|e| CodegenError::new(CodegenPhase::Lowering, "lower arithmetic runtime", e))?; codegen_trace("finish module"); - Ok(GeneratedModule { - module, - target_machine: tm, - }) + Ok(GeneratedModule { module, target_machine: tm }) } fn pipeline_from_opt_flag(opt_flag: &str) -> Result<&'static str, CodegenError> { @@ -1504,13 +1397,7 @@ fn pipeline_from_opt_flag(opt_flag: &str) -> Result<&'static str, CodegenError> "-O3" => "default", "-Os" => "default", "-Oz" => "default", - other => { - return Err(CodegenError::new( - CodegenPhase::Optimization, - "pass pipeline", - other, - )) - } + other => return Err(CodegenError::new(CodegenPhase::Optimization, "pass pipeline", other)), }) } @@ -1559,10 +1446,7 @@ fn add_enum_consts_to_globals( global_consts: &mut HashMap>, ) { let (bits, signed) = repr_bits_signed(&e.repr_type).unwrap_or_else(|| { - panic!( - "enum '{}' repr type must be an integer type, got {:?}", - e.name, e.repr_type - ) + panic!("enum '{}' repr type must be an integer type, got {:?}", e.name, e.repr_type) }); if bits > 64 || bits == 0 { diff --git a/llvm/src/codegen/legacy.rs b/llvm/src/codegen/legacy.rs index 780ad0dc..5fbb926f 100644 --- a/llvm/src/codegen/legacy.rs +++ b/llvm/src/codegen/legacy.rs @@ -25,12 +25,12 @@ use lexer::token::TokenType; pub fn get_llvm_type<'a>(context: &'a Context, ty: &TokenType) -> BasicTypeEnum<'a> { match ty { - TokenType::TypeInt(bits) => context - .custom_width_int_type(*bits as u32) - .as_basic_type_enum(), - TokenType::TypeUint(bits) => context - .custom_width_int_type(*bits as u32) - .as_basic_type_enum(), + TokenType::TypeInt(bits) => { + context.custom_width_int_type(*bits as u32).as_basic_type_enum() + }, + TokenType::TypeUint(bits) => { + context.custom_width_int_type(*bits as u32).as_basic_type_enum() + }, TokenType::TypeFloat(bits) => match bits { 32 => context.f32_type().as_basic_type_enum(), 64 => context.f64_type().as_basic_type_enum(), @@ -42,19 +42,13 @@ pub fn get_llvm_type<'a>(context: &'a Context, ty: &TokenType) -> BasicTypeEnum< TokenType::TypeByte => context.i8_type().as_basic_type_enum(), TokenType::TypePointer(inner_type) => { let _inner_llvm_type = get_llvm_type(context, inner_type); - context - .ptr_type(AddressSpace::default()) - .as_basic_type_enum() - } + context.ptr_type(AddressSpace::default()).as_basic_type_enum() + }, TokenType::TypeArray(inner_type, size) => { let inner_llvm_type = get_llvm_type(context, inner_type); - inner_llvm_type - .array_type(*size as u32) - .as_basic_type_enum() - } - TokenType::TypeString => context - .ptr_type(AddressSpace::default()) - .as_basic_type_enum(), + inner_llvm_type.array_type(*size as u32).as_basic_type_enum() + }, + TokenType::TypeString => context.ptr_type(AddressSpace::default()).as_basic_type_enum(), _ => panic!("Unsupported type: {:?}", ty), } } diff --git a/llvm/src/codegen/plan.rs b/llvm/src/codegen/plan.rs index 0da5acdc..aec4a4c6 100644 --- a/llvm/src/codegen/plan.rs +++ b/llvm/src/codegen/plan.rs @@ -87,10 +87,7 @@ fn normalize_token(s: &str) -> String { fn parse_token(target: CodegenTarget, raw: &str) -> RegToken { let raw_norm = normalize_token(raw); let phys_group = arch::operand_register_group(target.architecture(), &raw_norm); - RegToken { - raw_norm, - phys_group, - } + RegToken { raw_norm, phys_group } } fn is_valid_constraint_class(token: &str) -> bool { @@ -151,7 +148,7 @@ fn build_default_clobbers( } clobbers - } + }, } } @@ -194,24 +191,15 @@ fn normalize_special_clobber(target: CodegenTarget, token: &str) -> Option bool { - matches!( - normalize_token(token).as_str(), - "stack" | "uses_stack" | "uses-stack" - ) + matches!(normalize_token(token).as_str(), "stack" | "uses_stack" | "uses-stack") } fn is_nostack_pseudo_clobber(token: &str) -> bool { - matches!( - normalize_token(token).as_str(), - "nostack" | "no_stack" | "no-stack" - ) + matches!(normalize_token(token).as_str(), "nostack" | "no_stack" | "no-stack") } fn is_noreturn_pseudo_clobber(token: &str) -> bool { - matches!( - normalize_token(token).as_str(), - "noreturn" | "no_return" | "no-return" - ) + matches!(normalize_token(token).as_str(), "noreturn" | "no_return" | "no-return") } fn normalize_clobber_item(target: CodegenTarget, s: &str) -> Result { @@ -320,15 +308,11 @@ fn stack_contract_from_user_clobbers(user: &[String]) -> Result arch::StackAnalysis { diff --git a/llvm/src/codegen/platform/android.rs b/llvm/src/codegen/platform/android.rs index 574e27a8..e4890d1a 100644 --- a/llvm/src/codegen/platform/android.rs +++ b/llvm/src/codegen/platform/android.rs @@ -14,11 +14,8 @@ use super::{LinkerFamily, PlatformPlan}; -pub const CANDIDATE_TRIPLES: &[&str] = &[ - "x86_64-linux-android", - "aarch64-linux-android", - "riscv64-linux-android", -]; +pub const CANDIDATE_TRIPLES: &[&str] = + &["x86_64-linux-android", "aarch64-linux-android", "riscv64-linux-android"]; pub const PLAN: PlatformPlan = PlatformPlan { os: "android", diff --git a/llvm/src/codegen/platform/mod.rs b/llvm/src/codegen/platform/mod.rs index 93242bf0..e72f6b38 100644 --- a/llvm/src/codegen/platform/mod.rs +++ b/llvm/src/codegen/platform/mod.rs @@ -58,10 +58,7 @@ mod tests { assert_eq!(roadmap_platform("ios"), Some(&ios::PLAN)); assert_eq!(roadmap_platform("linux"), None); - for triple in android::CANDIDATE_TRIPLES - .iter() - .chain(ios::CANDIDATE_TRIPLES.iter()) - { + for triple in android::CANDIDATE_TRIPLES.iter().chain(ios::CANDIDATE_TRIPLES.iter()) { assert_eq!(target_spec_for_triple(triple), None, "{triple}"); } } diff --git a/llvm/src/codegen/target.rs b/llvm/src/codegen/target.rs index 3bc28b17..a455f8bb 100644 --- a/llvm/src/codegen/target.rs +++ b/llvm/src/codegen/target.rs @@ -119,16 +119,16 @@ pub fn resolve_target_options( Some("lp64") => { enabled.insert("f", false); enabled.insert("d", false); - } + }, Some("lp64f") => { enabled.insert("f", true); enabled.insert("d", false); - } + }, Some("lp64d") => { enabled.insert("f", true); enabled.insert("d", true); - } - _ => {} + }, + _ => {}, } } @@ -139,16 +139,16 @@ pub fn resolve_target_options( Some("lp64s") => { enabled.insert("f", false); enabled.insert("d", false); - } + }, Some("lp64f") => { enabled.insert("f", true); enabled.insert("d", false); - } + }, Some("lp64d") => { enabled.insert("f", true); enabled.insert("d", true); - } - _ => {} + }, + _ => {}, } } @@ -312,10 +312,8 @@ pub fn resolve_target_options( // RISC-V passes every supported feature with an explicit sign. Omitting a // disabled F/D feature can let LLVM's CPU defaults silently contradict the // effective ABI. - let render_all_features = matches!( - spec.architecture, - Architecture::Riscv64 | Architecture::LoongArch64 - ); + let render_all_features = + matches!(spec.architecture, Architecture::Riscv64 | Architecture::LoongArch64); let features = spec .features .iter() @@ -325,22 +323,13 @@ pub fn resolve_target_options( || spec.default_features.contains(name) }) .map(|name| { - let sign = if enabled.get(name).copied().unwrap_or(false) { - '+' - } else { - '-' - }; + let sign = if enabled.get(name).copied().unwrap_or(false) { '+' } else { '-' }; format!("{}{}", sign, name) }) .collect::>() .join(","); - Ok(EffectiveTargetOptions { - cpu: cpu.to_string(), - features, - abi: effective_abi, - isa, - }) + Ok(EffectiveTargetOptions { cpu: cpu.to_string(), features, abi: effective_abi, isa }) } #[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))] @@ -678,11 +667,7 @@ pub fn supported_target_specs() -> Vec<&'static TargetSpec> { specs.push(&LINUX_LOONGARCH64); #[cfg(any(feature = "llvm-target-all", feature = "llvm-target-wasm"))] - specs.extend([ - &WASM32_UNKNOWN_UNKNOWN, - &WASM32_WASIP1, - &WASM64_UNKNOWN_UNKNOWN, - ]); + specs.extend([&WASM32_UNKNOWN_UNKNOWN, &WASM32_WASIP1, &WASM64_UNKNOWN_UNKNOWN]); specs.sort_unstable_by_key(|spec| spec.triple); specs @@ -690,9 +675,7 @@ pub fn supported_target_specs() -> Vec<&'static TargetSpec> { /// Performs an exact lookup in the compiled target registry. pub fn target_spec_for_triple(triple: &str) -> Option<&'static TargetSpec> { - supported_target_specs() - .into_iter() - .find(|spec| spec.triple == triple) + supported_target_specs().into_iter().find(|spec| spec.triple == triple) } /// Returns the LLVM triple spelling whose environment encodes the selected @@ -725,7 +708,7 @@ impl CodegenTarget { | Self::FreestandingArm64 => Architecture::Aarch64, Self::LinuxRISCV64 | Self::FreeBsdRISCV64 | Self::FreestandingRISCV64 => { Architecture::Riscv64 - } + }, Self::LinuxLoongArch64 => Architecture::LoongArch64, Self::Wasm32Unknown | Self::Wasm32WasiP1 => Architecture::Wasm32, Self::Wasm64Unknown => Architecture::Wasm64, @@ -775,15 +758,9 @@ pub fn require_supported_target_from_triple(triple: &TargetTriple) -> CodegenTar } let raw = triple.as_str().to_string_lossy(); - let supported = supported_target_specs() - .into_iter() - .map(|spec| spec.triple) - .collect::>() - .join(", "); - panic!( - "unsupported target triple '{}': Wave currently supports {}", - raw, supported - ); + let supported = + supported_target_specs().into_iter().map(|spec| spec.triple).collect::>().join(", "); + panic!("unsupported target triple '{}': Wave currently supports {}", raw, supported); } pub fn require_supported_target_from_module(module: &Module<'_>) -> CodegenTarget { @@ -793,15 +770,9 @@ pub fn require_supported_target_from_module(module: &Module<'_>) -> CodegenTarge let triple = module.get_triple(); let raw = triple.as_str().to_string_lossy(); - let supported = supported_target_specs() - .into_iter() - .map(|spec| spec.triple) - .collect::>() - .join(", "); - panic!( - "unsupported target triple '{}': Wave currently supports {}", - raw, supported - ); + let supported = + supported_target_specs().into_iter().map(|spec| spec.triple).collect::>().join(", "); + panic!("unsupported target triple '{}': Wave currently supports {}", raw, supported); } #[cfg(test)] @@ -815,10 +786,7 @@ mod tests { for (index, spec) in specs.iter().enumerate() { assert_eq!(target_spec_for_triple(spec.triple), Some(*spec)); - assert_eq!( - CodegenTarget::from_triple_str(spec.triple), - Some(spec.codegen) - ); + assert_eq!(CodegenTarget::from_triple_str(spec.triple), Some(spec.codegen)); assert!(!spec.architecture.name().is_empty()); assert!(!spec.os.is_empty()); assert!(!spec.object_format.is_empty()); @@ -913,10 +881,7 @@ mod tests { for abi in ["lp64s", "lp64f"] { let error = resolve_target_options(&LINUX_LOONGARCH64, None, Some("+lsx"), Some(abi)) .unwrap_err(); - assert!( - error.contains("feature 'lsx' requires feature 'd'"), - "{error}" - ); + assert!(error.contains("feature 'lsx' requires feature 'd'"), "{error}"); } let error = diff --git a/llvm/src/codegen/types.rs b/llvm/src/codegen/types.rs index c21cf6d9..93a07900 100644 --- a/llvm/src/codegen/types.rs +++ b/llvm/src/codegen/types.rs @@ -58,10 +58,10 @@ pub fn wave_type_to_llvm_type<'ctx>( match wave_type { WaveType::Isz | WaveType::Usz | WaveType::Never => { unreachable!("typed HIR must resolve target-sized integers") - } - WaveType::Int(bits) | WaveType::Uint(bits) => context - .custom_width_int_type(*bits as u32) - .as_basic_type_enum(), + }, + WaveType::Int(bits) | WaveType::Uint(bits) => { + context.custom_width_int_type(*bits as u32).as_basic_type_enum() + }, WaveType::Float(bits) => match bits { 32 => context.f32_type().as_basic_type_enum(), @@ -75,25 +75,21 @@ pub fn wave_type_to_llvm_type<'ctx>( } else { context.bool_type().as_basic_type_enum() } - } + }, WaveType::Char | WaveType::Byte => context.i8_type().as_basic_type_enum(), WaveType::Void => context.i8_type().as_basic_type_enum(), WaveType::Future(_) => context.i64_type().as_basic_type_enum(), - WaveType::Pointer(_inner) => context - .ptr_type(AddressSpace::default()) - .as_basic_type_enum(), + WaveType::Pointer(_inner) => context.ptr_type(AddressSpace::default()).as_basic_type_enum(), WaveType::Array(inner, size) => { let inner_ty = wave_type_to_llvm_type(context, inner, struct_types, flavor); inner_ty.array_type(*size as u32).as_basic_type_enum() - } + }, - WaveType::String => context - .ptr_type(AddressSpace::default()) - .as_basic_type_enum(), + WaveType::String => context.ptr_type(AddressSpace::default()).as_basic_type_enum(), WaveType::Struct(name) => struct_types .get(name) @@ -113,13 +109,13 @@ fn flatten_leaves<'ctx>(t: BasicTypeEnum<'ctx>, out: &mut Vec { let elem = at.get_element_type(); for _ in 0..at.len() { flatten_leaves(elem, out); } - } + }, _ => out.push(t), } } @@ -129,13 +125,10 @@ fn is_integer_only_aggregate<'ctx>(t: BasicTypeEnum<'ctx>) -> bool { BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_) => { let mut leaves = vec![]; flatten_leaves(t, &mut leaves); - leaves.iter().all(|lt| { - matches!( - lt, - BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_) - ) - }) - } + leaves + .iter() + .all(|lt| matches!(lt, BasicTypeEnum::IntType(_) | BasicTypeEnum::PointerType(_))) + }, _ => false, } } diff --git a/llvm/src/codegen/validation.rs b/llvm/src/codegen/validation.rs index e46dc8b8..c51bd402 100644 --- a/llvm/src/codegen/validation.rs +++ b/llvm/src/codegen/validation.rs @@ -8,11 +8,7 @@ use hir::TypedProgram; use parser::ast::{ASTNode, Expression, StatementNode}; pub(crate) fn validate(program: &TypedProgram, target: CodegenTarget) -> Result<(), CodegenError> { - let mut state = State { - program, - target, - error: None, - }; + let mut state = State { program, target, error: None }; walk_nodes(program.syntax(), &mut state); state.error.map_or(Ok(()), Err) } @@ -56,6 +52,7 @@ impl State<'_> { ); } } + fn node(&mut self, node: &ASTNode) { if self.error.is_some() { return; @@ -67,25 +64,17 @@ impl State<'_> { clobbers, }) = node { - let span = self - .program - .node_id(node) - .and_then(|id| self.program.node_span(id)) - .cloned(); + let span = + self.program.node_id(node).and_then(|id| self.program.node_span(id)).cloned(); self.block(instructions, inputs, outputs, clobbers, false, span); } } + fn expression(&mut self, expression: &Expression) { if self.error.is_some() { return; } - if let Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } = expression - { + if let Expression::AsmBlock { instructions, inputs, outputs, clobbers } = expression { let span = self .program .expression_id(expression) @@ -116,7 +105,7 @@ fn walk_node(node: &ASTNode, state: &mut State<'_>) { } } walk_nodes(&function.body, state); - } + }, ASTNode::Struct(structure) => { if !structure.generic_params.is_empty() { return; @@ -129,7 +118,7 @@ fn walk_node(node: &ASTNode, state: &mut State<'_>) { } walk_nodes(&method.body, state); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { for parameter in &method.parameters { @@ -139,19 +128,19 @@ fn walk_node(node: &ASTNode, state: &mut State<'_>) { } walk_nodes(&method.body, state); } - } + }, ASTNode::Statement(statement) => walk_statement(statement, state), ASTNode::Variable(variable) => { if let Some(initializer) = &variable.initial_value { walk_expression(initializer, state); } - } + }, ASTNode::Expression(expression) => walk_expression(expression, state), ASTNode::ExternFunction(_) | ASTNode::Program(_) | ASTNode::TypeAlias(_) | ASTNode::Enum(_) - | ASTNode::Variant(_) => {} + | ASTNode::Variant(_) => {}, } } @@ -163,13 +152,8 @@ fn walk_statement(statement: &StatementNode, state: &mut State<'_>) { for argument in args { walk_expression(argument, state); } - } - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + }, + StatementNode::If { condition, body, else_if_blocks, else_block } => { walk_expression(condition, state); walk_nodes(body, state); if let Some(blocks) = else_if_blocks { @@ -181,46 +165,39 @@ fn walk_statement(statement: &StatementNode, state: &mut State<'_>) { if let Some(body) = else_block { walk_nodes(body, state); } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + }, + StatementNode::For { initialization, condition, increment, body } => { walk_node(initialization, state); walk_expression(condition, state); walk_expression(increment, state); walk_nodes(body, state); - } + }, StatementNode::While { condition, body } => { walk_expression(condition, state); walk_nodes(body, state); - } + }, StatementNode::Match { value, arms } => { walk_expression(value, state); for arm in arms { walk_nodes(&arm.body, state); } - } + }, StatementNode::Assign { value, .. } => walk_expression(value, state), - StatementNode::AsmBlock { - inputs, outputs, .. - } => { + StatementNode::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter().chain(outputs.iter()) { walk_expression(expression, state); } - } + }, StatementNode::Return(Some(expression)) | StatementNode::Expression(expression) => { walk_expression(expression, state) - } + }, StatementNode::Print(_) | StatementNode::Println(_) | StatementNode::Variable(_) | StatementNode::Import(_) | StatementNode::Break | StatementNode::Continue - | StatementNode::Return(None) => {} + | StatementNode::Return(None) => {}, } } @@ -239,18 +216,18 @@ fn walk_expression(expression: &Expression, state: &mut State<'_>) { for (_, value) in fields { walk_expression(value, state); } - } + }, Expression::FunctionCall { args, .. } => { for argument in args { walk_expression(argument, state); } - } + }, Expression::MethodCall { object, args, .. } => { walk_expression(object, state); for argument in args { walk_expression(argument, state); } - } + }, Expression::Deref(inner) | Expression::AddressOf(inner) | Expression::Await(inner) @@ -260,34 +237,22 @@ fn walk_expression(expression: &Expression, state: &mut State<'_>) { | Expression::FieldAccess { object: inner, .. } | Expression::IncDec { target: inner, .. } => walk_expression(inner, state), Expression::BinaryExpression { left, right, .. } - | Expression::IndexAccess { - target: left, - index: right, - } - | Expression::AssignOperation { - target: left, - value: right, - .. - } - | Expression::Assignment { - target: left, - value: right, - } => { + | Expression::IndexAccess { target: left, index: right } + | Expression::AssignOperation { target: left, value: right, .. } + | Expression::Assignment { target: left, value: right } => { walk_expression(left, state); walk_expression(right, state); - } + }, Expression::ArrayLiteral(values) => { for value in values { walk_expression(value, state); } - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, expression) in inputs.iter().chain(outputs.iter()) { walk_expression(expression, state); } - } - Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {} + }, + Expression::Null | Expression::Literal(_) | Expression::Variable(_) => {}, } } diff --git a/llvm/src/codegen/variants.rs b/llvm/src/codegen/variants.rs index 4e58c315..075662dc 100644 --- a/llvm/src/codegen/variants.rs +++ b/llvm/src/codegen/variants.rs @@ -66,19 +66,12 @@ pub(crate) fn declare_variant_types<'ctx>( collect_type_variants(payload, &mut names); } } - for variant in templates - .values() - .filter(|variant| variant.generic_params.is_empty()) - { + for variant in templates.values().filter(|variant| variant.generic_params.is_empty()) { names.insert(variant.name.clone()); } let mut definitions = BTreeMap::>>::new(); - while let Some(name) = names - .iter() - .find(|name| !definitions.contains_key(*name)) - .cloned() - { + while let Some(name) = names.iter().find(|name| !definitions.contains_key(*name)).cloned() { let cases = specialize_variant(&name, &templates); for case in &cases { for payload in case { @@ -94,10 +87,7 @@ pub(crate) fn declare_variant_types<'ctx>( let llvm_name = format!("variant.{name}"); let ty = context.opaque_struct_type(&llvm_name); if struct_types.insert(name.clone(), ty).is_some() { - panic!( - "variant type '{}' conflicts with another LLVM aggregate", - name - ); + panic!("variant type '{}' conflicts with another LLVM aggregate", name); } VariantDefinition { name, cases } }) @@ -158,10 +148,7 @@ pub(crate) fn define_variant_types<'ctx>( ); false }); - assert!( - pending.len() < before, - "validated variants contain an unsized value cycle" - ); + assert!(pending.len() < before, "validated variants contain an unsized value cycle"); } } @@ -169,21 +156,15 @@ fn specialize_variant( concrete_name: &str, templates: &HashMap, ) -> Vec> { - if let Some(concrete) = templates - .get(concrete_name) - .filter(|variant| variant.generic_params.is_empty()) + if let Some(concrete) = + templates.get(concrete_name).filter(|variant| variant.generic_params.is_empty()) { - return concrete - .cases - .iter() - .map(|case| case.payload_types.clone()) - .collect(); + return concrete.cases.iter().map(|case| case.payload_types.clone()).collect(); } let (base, arguments) = split_variant_application(concrete_name) .unwrap_or_else(|| (concrete_name.to_string(), Vec::new())); - let template = templates - .get(&base) - .unwrap_or_else(|| panic!("variant template '{}' not found", base)); + let template = + templates.get(&base).unwrap_or_else(|| panic!("variant template '{}' not found", base)); if template.generic_params.len() != arguments.len() { panic!( "variant '{}' expects {} type arguments, found {}", @@ -192,12 +173,8 @@ fn specialize_variant( arguments.len() ); } - let substitutions = template - .generic_params - .iter() - .cloned() - .zip(arguments) - .collect::>(); + let substitutions = + template.generic_params.iter().cloned().zip(arguments).collect::>(); template .cases .iter() @@ -216,16 +193,12 @@ fn resolve_payload_type( templates: &HashMap, ) -> WaveType { match ty { - WaveType::Future(inner) => WaveType::Future(Box::new(resolve_payload_type( - inner, - substitutions, - templates, - ))), - WaveType::Pointer(inner) => WaveType::Pointer(Box::new(resolve_payload_type( - inner, - substitutions, - templates, - ))), + WaveType::Future(inner) => { + WaveType::Future(Box::new(resolve_payload_type(inner, substitutions, templates))) + }, + WaveType::Pointer(inner) => { + WaveType::Pointer(Box::new(resolve_payload_type(inner, substitutions, templates))) + }, WaveType::Array(inner, length) => WaveType::Array( Box::new(resolve_payload_type(inner, substitutions, templates)), *length, @@ -248,18 +221,14 @@ fn resolve_payload_type( let concrete = format!( "{}<{}>", base, - arguments - .iter() - .map(display_wave_type) - .collect::>() - .join(",") + arguments.iter().map(display_wave_type).collect::>().join(",") ); if templates.contains_key(&base) { WaveType::Variant(concrete) } else { WaveType::Struct(concrete) } - } + }, _ => ty.clone(), } } @@ -298,11 +267,11 @@ fn collect_type_variants(ty: &WaveType, names: &mut BTreeSet) { match ty { WaveType::Variant(name) => { names.insert(name.clone()); - } + }, WaveType::Future(inner) | WaveType::Pointer(inner) | WaveType::Array(inner, _) => { collect_type_variants(inner, names) - } - _ => {} + }, + _ => {}, } } @@ -318,13 +287,13 @@ fn collect_node_variant_types(nodes: &[ASTNode], names: &mut BTreeSet) { collect_type_variants(return_type, names); } collect_node_variant_types(&function.body, names); - } + }, ASTNode::ExternFunction(function) => { for (_, ty) in &function.params { collect_type_variants(ty, names); } collect_type_variants(&function.return_type, names); - } + }, ASTNode::Program(parameter) => collect_type_variants(¶meter.param_type, names), ASTNode::Variable(variable) => collect_type_variants(&variable.type_name, names), ASTNode::Statement(statement) => collect_statement_variant_types(statement, names), @@ -341,7 +310,7 @@ fn collect_node_variant_types(nodes: &[ASTNode], names: &mut BTreeSet) { } collect_node_variant_types(&method.body, names); } - } + }, ASTNode::ProtoImpl(implementation) => { for method in &implementation.methods { if let Some(return_type) = &method.return_type { @@ -352,22 +321,17 @@ fn collect_node_variant_types(nodes: &[ASTNode], names: &mut BTreeSet) { } collect_node_variant_types(&method.body, names); } - } + }, ASTNode::TypeAlias(alias) => collect_type_variants(&alias.target, names), ASTNode::Enum(enumeration) => collect_type_variants(&enumeration.repr_type, names), - ASTNode::Variant(_) | ASTNode::Expression(_) => {} + ASTNode::Variant(_) | ASTNode::Expression(_) => {}, } } } fn collect_statement_variant_types(statement: &StatementNode, names: &mut BTreeSet) { match statement { - StatementNode::If { - body, - else_if_blocks, - else_block, - .. - } => { + StatementNode::If { body, else_if_blocks, else_block, .. } => { collect_node_variant_types(body, names); if let Some(blocks) = else_if_blocks { for (_, body) in blocks.iter() { @@ -377,22 +341,18 @@ fn collect_statement_variant_types(statement: &StatementNode, names: &mut BTreeS if let Some(body) = else_block { collect_node_variant_types(body, names); } - } + }, StatementNode::While { body, .. } => collect_node_variant_types(body, names), - StatementNode::For { - initialization, - body, - .. - } => { + StatementNode::For { initialization, body, .. } => { collect_node_variant_types(std::slice::from_ref(initialization.as_ref()), names); collect_node_variant_types(body, names); - } + }, StatementNode::Match { arms, .. } => { for arm in arms { collect_node_variant_types(&arm.body, names); } - } - _ => {} + }, + _ => {}, } } @@ -421,7 +381,7 @@ pub(crate) fn constant_storage_bytes<'ctx>( )?; } return Ok(()); - } + }, BasicValueEnum::ArrayValue(array) => { let ty = array.get_type(); let stride = td.get_abi_size(&ty.get_element_type()) as usize; @@ -441,8 +401,8 @@ pub(crate) fn constant_storage_bytes<'ctx>( )?; } return Ok(()); - } - _ => {} + }, + _ => {}, } let size = td.get_store_size(&value.get_type()) as usize; let integer = match value { @@ -456,7 +416,7 @@ pub(crate) fn constant_storage_bytes<'ctx>( ty.as_type_ref(), )) } - } + }, BasicValueEnum::PointerValue(pointer) => { let ty = context.custom_width_int_type(size as u32 * 8); // LLVM does not fold ptrtoint(inttoptr(i64)) to i32 without @@ -473,26 +433,21 @@ pub(crate) fn constant_storage_bytes<'ctx>( pointer.const_to_int(ty) } } - } + }, _ => { return Err(ConstEvalError::Unsupported( "variant constant contains unsupported storage".into(), )) - } + }, }; let printed = integer.print_to_string().to_string(); - let raw = printed - .split_once(' ') - .map(|(_, value)| value) - .unwrap_or(""); + let raw = printed.split_once(' ').map(|(_, value)| value).unwrap_or(""); let raw = match raw { "true" => "1", "false" => "0", other => other, }; - let (negative, digits) = raw - .strip_prefix('-') - .map_or((false, raw), |digits| (true, digits)); + let (negative, digits) = raw.strip_prefix('-').map_or((false, raw), |digits| (true, digits)); if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) { return Err(ConstEvalError::Unsupported(format!( "variant constant requires numeric storage: {printed}" diff --git a/llvm/src/diagnostic.rs b/llvm/src/diagnostic.rs index 34508ae2..9424778c 100644 --- a/llvm/src/diagnostic.rs +++ b/llvm/src/diagnostic.rs @@ -52,6 +52,7 @@ impl CodegenError { span: None, } } + pub fn tool_launch(phase: CodegenPhase, tool: &str, error: std::io::Error) -> Self { let missing = error.kind() == std::io::ErrorKind::NotFound; let mut diagnostic = Self::new(phase, format!("launch {tool}"), error); @@ -60,10 +61,12 @@ impl CodegenError { } diagnostic } + pub fn invalid_assembly(mut self) -> Self { self.kind = CodegenErrorKind::InvalidAssembly; self } + pub fn with_span(mut self, span: Option) -> Self { self.span = span.map(Box::new); self @@ -90,35 +93,20 @@ pub struct PendingOutput { impl PendingOutput { pub fn new(destination: &Path) -> Result { static NEXT: AtomicU64 = AtomicU64::new(0); - let parent = destination - .parent() - .filter(|p| !p.as_os_str().is_empty()) - .unwrap_or(Path::new(".")); + let parent = + destination.parent().filter(|p| !p.as_os_str().is_empty()).unwrap_or(Path::new(".")); for _ in 0..128 { let path = parent.join(format!( ".wave-output-{}-{}.tmp", std::process::id(), NEXT.fetch_add(1, Ordering::Relaxed) )); - match std::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&path) - { - Ok(_) => { - return Ok(Self { - path, - destination: destination.to_owned(), - }) - } + match std::fs::OpenOptions::new().write(true).create_new(true).open(&path) { + Ok(_) => return Ok(Self { path, destination: destination.to_owned() }), Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue, Err(e) => { - return Err(CodegenError::new( - CodegenPhase::Emission, - "create output", - e, - )) - } + return Err(CodegenError::new(CodegenPhase::Emission, "create output", e)) + }, } } Err(CodegenError::new( @@ -127,9 +115,11 @@ impl PendingOutput { "temporary output name collision", )) } + pub fn path(&self) -> &Path { &self.path } + pub fn commit(self) -> Result<(), CodegenError> { let length = std::fs::metadata(&self.path) .map_err(|e| CodegenError::new(CodegenPhase::Emission, "inspect output", e))? diff --git a/llvm/src/expression/lvalue.rs b/llvm/src/expression/lvalue.rs index 836f8441..95f04991 100644 --- a/llvm/src/expression/lvalue.rs +++ b/llvm/src/expression/lvalue.rs @@ -43,7 +43,7 @@ fn llvm_basic_to_wave_type<'ctx>(bt: BasicTypeEnum<'ctx>) -> WaveType { } else { WaveType::Int(w as usize as u16) } - } + }, BasicTypeEnum::FloatType(ft) => { let bw = ft.get_bit_width(); match bw { @@ -51,14 +51,11 @@ fn llvm_basic_to_wave_type<'ctx>(bt: BasicTypeEnum<'ctx>) -> WaveType { 64 => WaveType::Float(64), _ => panic!("Unsupported float width: {}", bw), } - } + }, BasicTypeEnum::ArrayType(at) => { let elem = at.get_element_type(); - WaveType::Array( - Box::new(llvm_basic_to_wave_type(elem)), - at.len() as usize as u32, - ) - } + WaveType::Array(Box::new(llvm_basic_to_wave_type(elem)), at.len() as usize as u32) + }, BasicTypeEnum::StructType(st) => { let raw = st .get_name() @@ -66,11 +63,11 @@ fn llvm_basic_to_wave_type<'ctx>(bt: BasicTypeEnum<'ctx>) -> WaveType { .unwrap_or_else(|| panic!("Unnamed struct type; cannot map to WaveType")); let name = raw.strip_prefix("struct.").unwrap_or(raw).to_string(); WaveType::Struct(name) - } + }, BasicTypeEnum::PointerType(_) => WaveType::Pointer(Box::new(WaveType::Void)), BasicTypeEnum::VectorType(_) | BasicTypeEnum::ScalableVectorType(_) => { panic!("Vector types are not supported in WaveType mapping yet"); - } + }, } } @@ -81,10 +78,7 @@ fn load_ptr_value<'ctx>( name: &str, ) -> PointerValue<'ctx> { let ptr_ty = context.ptr_type(AddressSpace::default()); - builder - .build_load(ptr_ty, addr_of_ptr, name) - .unwrap() - .into_pointer_value() + builder.build_load(ptr_ty, addr_of_ptr, name).unwrap().into_pointer_value() } fn generate_lvalue_ir_typed<'ctx>( @@ -106,12 +100,11 @@ fn generate_lvalue_ir_typed<'ctx>( panic!("Cannot use constant '{}' as lvalue", name); } - let info = variables - .get(name) - .unwrap_or_else(|| panic!("Undefined variable '{}'", name)); + let info = + variables.get(name).unwrap_or_else(|| panic!("Undefined variable '{}'", name)); (info.ptr, info.ty.clone()) - } + }, Expression::Grouped(inner) => generate_lvalue_ir_typed( program, @@ -177,7 +170,7 @@ fn generate_lvalue_ir_typed<'ctx>( }; (p, pointee_ty) - } + }, Expression::AddressOf(inner) => generate_lvalue_ir_typed( program, @@ -251,10 +244,10 @@ fn generate_lvalue_ir_typed<'ctx>( Target expression type info is missing: {:?}", target ); - } + }, _ => panic!("Index target is not a pointer/array: {:?}", target), } - } + }, }; let zero = idx.get_type().const_zero(); @@ -274,7 +267,7 @@ fn generate_lvalue_ir_typed<'ctx>( .unwrap() }; (gep, *inner) - } + }, WaveType::Pointer(inner) => { let base_ptr = load_ptr_value(context, builder, base_addr, "load_index_base"); @@ -298,7 +291,7 @@ fn generate_lvalue_ir_typed<'ctx>( .unwrap() }; (gep, *element) - } + }, element => { let elem_llvm = wave_type_to_llvm_type( context, @@ -312,9 +305,9 @@ fn generate_lvalue_ir_typed<'ctx>( .unwrap() }; (gep, element) - } + }, } - } + }, WaveType::String => { let base_ptr = load_ptr_value(context, builder, base_addr, "load_index_base"); @@ -325,16 +318,13 @@ fn generate_lvalue_ir_typed<'ctx>( .unwrap() }; (gep, WaveType::Byte) - } + }, other => { - panic!( - "IndexAccess target is not array/pointer/string: {:?}", - other - ); - } + panic!("IndexAccess target is not array/pointer/string: {:?}", other); + }, } - } + }, Expression::FieldAccess { object, field } => { let (obj_addr, obj_ty) = generate_lvalue_ir_typed( @@ -358,14 +348,11 @@ fn generate_lvalue_ir_typed<'ctx>( WaveType::Struct(name) => { let p = load_ptr_value(context, builder, obj_addr, "load_struct_ptr"); (p, name) - } + }, other => panic!("FieldAccess base pointer is not ptr: {:?}", other), }, - other => panic!( - "FieldAccess base is not a struct or ptr: {:?}", - other - ), + other => panic!("FieldAccess base is not a struct or ptr: {:?}", other), }; let struct_ty = struct_types @@ -378,9 +365,8 @@ fn generate_lvalue_ir_typed<'ctx>( .copied() .unwrap_or_else(|| panic!("Unknown field '{}.{}'", struct_name, field)); - let field_ptr = builder - .build_struct_gep(*struct_ty, obj_ptr, field_index, "field_ptr") - .unwrap(); + let field_ptr = + builder.build_struct_gep(*struct_ty, obj_ptr, field_index, "field_ptr").unwrap(); let field_bt = struct_ty .get_field_type_at_index(field_index) @@ -394,11 +380,11 @@ fn generate_lvalue_ir_typed<'ctx>( }; (field_ptr, field_wave_ty) - } + }, _ => { panic!("Expression is not an lvalue (not assignable): {:?}", expr); - } + }, } } diff --git a/llvm/src/expression/rvalue/arrays.rs b/llvm/src/expression/rvalue/arrays.rs index fb555f4f..c41506af 100644 --- a/llvm/src/expression/rvalue/arrays.rs +++ b/llvm/src/expression/rvalue/arrays.rs @@ -35,7 +35,7 @@ pub(crate) fn gen_array_literal<'ctx, 'a>( "ArrayLiteral cannot use pointer expected_type on opaque-pointer LLVM. \ Use a temp variable: `var tmp: array = [...]; foo(tmp);`" ); - } + }, Some(t) => panic!("ArrayLiteral expected array type, got {:?}", t), None => panic!("ArrayLiteral requires expected_type (array type)"), @@ -74,8 +74,5 @@ Use a temp variable: `var tmp: array = [...]; foo(tmp);`" env.builder.build_store(gep, v).unwrap(); } - env.builder - .build_load(arr_ty, alloca, "arr_lit_load") - .unwrap() - .as_basic_value_enum() + env.builder.build_load(arr_ty, alloca, "arr_lit_load").unwrap().as_basic_value_enum() } diff --git a/llvm/src/expression/rvalue/asm.rs b/llvm/src/expression/rvalue/asm.rs index d7536fd8..a5cdf78d 100644 --- a/llvm/src/expression/rvalue/asm.rs +++ b/llvm/src/expression/rvalue/asm.rs @@ -77,23 +77,14 @@ pub(crate) fn gen<'ctx, 'a>( // context and function signature used by `build_indirect_call` below. let callee = unsafe { PointerValue::new(inline_asm.as_value_ref()) }; - env.builder - .build_indirect_call(fn_type, callee, &operand_vals, "inline_asm_void") - .unwrap(); - - return env - .context - .i64_type() - .const_int(0, false) - .as_basic_value_enum(); + env.builder.build_indirect_call(fn_type, callee, &operand_vals, "inline_asm_void").unwrap(); + + return env.context.i64_type().const_int(0, false).as_basic_value_enum(); } // asm expr must have exactly 1 output if plan.outputs.len() != 1 { - panic!( - "asm expression requires exactly 1 output (got {})", - plan.outputs.len() - ); + panic!("asm expression requires exactly 1 output (got {})", plan.outputs.len()); } let out_ty = resolve_expr_out_type(env, plan.outputs[0].target); @@ -113,16 +104,14 @@ pub(crate) fn gen<'ctx, 'a>( // which is also supplied to the indirect call immediately below. let callee = unsafe { PointerValue::new(inline_asm.as_value_ref()) }; - let call = env - .builder - .build_indirect_call(fn_type, callee, &operand_vals, "inline_asm_expr") - .unwrap(); + let call = + env.builder.build_indirect_call(fn_type, callee, &operand_vals, "inline_asm_expr").unwrap(); match call.try_as_basic_value() { ValueKind::Basic(v) => v, ValueKind::Instruction(_) => { panic!("inline asm expr expected to return a value, but got instruction-only result"); - } + }, } } @@ -134,11 +123,7 @@ fn resolve_expr_out_type<'ctx, 'a>( env: &ExprGenEnv<'ctx, 'a>, target: &Expression, ) -> BasicTypeEnum<'ctx> { - llvm_type_of_wave( - env, - &env.wave_type(target) - .expect("asm output has a validated HIR type"), - ) + llvm_type_of_wave(env, &env.wave_type(target).expect("asm output has a validated HIR type")) } fn eval_asm_in_expr<'ctx, 'a>( @@ -163,6 +148,6 @@ fn meta_val_type<'ctx>(v: &BasicMetadataValueEnum<'ctx>) -> BasicMetadataTypeEnu BasicMetadataValueEnum::ScalableVectorValue(svv) => svv.get_type().into(), BasicMetadataValueEnum::MetadataValue(_) => { panic!("MetadataValue cannot be used as an inline asm operand"); - } + }, } } diff --git a/llvm/src/expression/rvalue/assign.rs b/llvm/src/expression/rvalue/assign.rs index e1663c43..9a63279a 100644 --- a/llvm/src/expression/rvalue/assign.rs +++ b/llvm/src/expression/rvalue/assign.rs @@ -27,9 +27,7 @@ use inkwell::AddressSpace; use parser::ast::{AssignOperator, Expression, WaveType}; fn normalize_struct_name(raw: &str) -> &str { - raw.strip_prefix("struct.") - .unwrap_or(raw) - .trim_start_matches('%') + raw.strip_prefix("struct.").unwrap_or(raw).trim_start_matches('%') } fn resolve_struct_key<'ctx, 'a>( @@ -66,13 +64,13 @@ fn basic_to_wave<'ctx, 'a>( } else { Some(WaveType::Int(bw)) } - } + }, BasicTypeEnum::FloatType(ft) => Some(WaveType::Float(ft.get_bit_width() as u16)), BasicTypeEnum::PointerType(_) => Some(WaveType::Pointer(Box::new(WaveType::Byte))), BasicTypeEnum::ArrayType(at) => { let elem = basic_to_wave(env, at.get_element_type())?; Some(WaveType::Array(Box::new(elem), at.len())) - } + }, BasicTypeEnum::StructType(st) => Some(WaveType::Struct(resolve_struct_key(env, st))), _ => None, } @@ -87,7 +85,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> Expression::Grouped(inner) => wave_type_of_lvalue(env, inner), Expression::AddressOf(inner) => { wave_type_of_lvalue(env, inner).map(|t| WaveType::Pointer(Box::new(t))) - } + }, Expression::Deref(inner) => { let inner_ty = wave_type_of_lvalue(env, inner)?; if matches!( @@ -101,7 +99,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> WaveType::String => Some(WaveType::Byte), other => Some(other), } - } + }, Expression::IndexAccess { target, .. } => { let t = wave_type_of_lvalue(env, target)?; match t { @@ -113,7 +111,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> WaveType::String => Some(WaveType::Byte), _ => None, } - } + }, Expression::FieldAccess { object, field } => { if let Some(hir::HirExpressionType::Resolved(ty)) = env.program.type_of(e) { return Some(ty.clone()); @@ -129,14 +127,11 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> }; let st = *env.struct_types.get(&struct_name)?; - let field_index = env - .struct_field_indices - .get(&struct_name) - .and_then(|m| m.get(field)) - .copied()?; + let field_index = + env.struct_field_indices.get(&struct_name).and_then(|m| m.get(field)).copied()?; let field_bt = st.get_field_type_at_index(field_index)?; basic_to_wave(env, field_bt) - } + }, _ => None, } } @@ -162,10 +157,7 @@ fn ensure_null_target_is_ptr<'ctx, 'a>( .unwrap_or_else(|| panic!("Cannot infer lvalue type for null assignment: {:?}", target)); if !matches!(target_ty, WaveType::Pointer(_)) { - panic!( - "null literal can only be assigned to ptr (target type: {:?})", - target_ty - ); + panic!("null literal can only be assigned to ptr (target type: {:?})", target_ty); } } @@ -176,13 +168,13 @@ fn infer_lvalue_store_type<'ctx, 'a>( match target { Expression::Grouped(inner) | Expression::AddressOf(inner) => { infer_lvalue_store_type(env, inner) - } + }, Expression::Variable(_) | Expression::Deref(_) | Expression::IndexAccess { .. } => { let wt = wave_type_of_lvalue(env, target) .unwrap_or_else(|| panic!("Cannot infer lvalue type: {:?}", target)); wave_to_basic(env, &wt) - } + }, Expression::FieldAccess { object, field } => { let struct_name_opt = wave_type_of_lvalue(env, object).and_then(|wt| match wt { @@ -208,10 +200,7 @@ fn infer_lvalue_store_type<'ctx, 'a>( .unwrap_or_else(|| panic!("Unknown field '{}.{}'", struct_name, field)); return st.get_field_type_at_index(field_index).unwrap_or_else(|| { - panic!( - "Invalid field index {} for struct {}", - field_index, struct_name - ) + panic!("Invalid field index {} for struct {}", field_index, struct_name) }); } @@ -234,12 +223,9 @@ fn infer_lvalue_store_type<'ctx, 'a>( .unwrap_or_else(|| panic!("Unknown field '{}.{}'", struct_key, field)); st.get_field_type_at_index(field_index).unwrap_or_else(|| { - panic!( - "Invalid field index {} for struct {}", - field_index, struct_key - ) + panic!("Invalid field index {} for struct {}", field_index, struct_key) }) - } + }, _ => panic!("Expression is not an assignable lvalue: {:?}", target), } @@ -252,16 +238,12 @@ fn materialize_for_store<'ctx, 'a>( tag: &str, ) -> BasicValueEnum<'ctx> { match (rhs, element_type) { - (BasicValueEnum::PointerValue(pv), BasicTypeEnum::ArrayType(at)) => env - .builder - .build_load(at, pv, tag) - .unwrap() - .as_basic_value_enum(), - (BasicValueEnum::PointerValue(pv), BasicTypeEnum::StructType(st)) => env - .builder - .build_load(st, pv, tag) - .unwrap() - .as_basic_value_enum(), + (BasicValueEnum::PointerValue(pv), BasicTypeEnum::ArrayType(at)) => { + env.builder.build_load(at, pv, tag).unwrap().as_basic_value_enum() + }, + (BasicValueEnum::PointerValue(pv), BasicTypeEnum::StructType(st)) => { + env.builder.build_load(st, pv, tag).unwrap().as_basic_value_enum() + }, (v, _) => v, } } @@ -290,16 +272,10 @@ fn try_gen_store_through_rvalue_pointer<'ctx, 'a>( let element_type = rhs.get_type(); rhs = materialize_for_store(env, rhs, element_type, "rvalue_ptr_assign_agg_load"); - let ptr_ty = env - .context - .ptr_type(AddressSpace::default()) - .as_basic_type_enum(); + let ptr_ty = env.context.ptr_type(AddressSpace::default()).as_basic_type_enum(); let ptr_val = env.gen(inner, Some(ptr_ty)); let BasicValueEnum::PointerValue(dst_ptr) = ptr_val else { - panic!( - "deref assignment target must evaluate to a pointer, got {:?}", - ptr_val.get_type() - ); + panic!("deref assignment target must evaluate to a pointer, got {:?}", ptr_val.get_type()); }; env.builder.build_store(dst_ptr, rhs).unwrap(); @@ -321,9 +297,7 @@ pub(crate) fn gen_assign_operation<'ctx, 'a>( let ptr = generate_address_ir(env, target); let element_type = infer_lvalue_store_type(env, target); - let target_wave_type = env - .wave_type(target) - .or_else(|| wave_type_of_lvalue(env, target)); + let target_wave_type = env.wave_type(target).or_else(|| wave_type_of_lvalue(env, target)); let target_unsigned = matches!( target_wave_type, Some(WaveType::Uint(_) | WaveType::Bool | WaveType::Byte | WaveType::Char) @@ -354,11 +328,8 @@ pub(crate) fn gen_assign_operation<'ctx, 'a>( } // compound op: load current typed - let current_val = env - .builder - .build_load(element_type, ptr, "load_current") - .unwrap() - .as_basic_value_enum(); + let current_val = + env.builder.build_load(element_type, ptr, "load_current").unwrap().as_basic_value_enum(); let new_val = env.gen(value, Some(current_val.get_type())); @@ -370,21 +341,15 @@ pub(crate) fn gen_assign_operation<'ctx, 'a>( let result = match (current_val, new_val) { (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) => match operator { - AssignOperator::AddAssign => env - .builder - .build_int_add(lhs, rhs, "add_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::SubAssign => env - .builder - .build_int_sub(lhs, rhs, "sub_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::MulAssign => env - .builder - .build_int_mul(lhs, rhs, "mul_assign") - .unwrap() - .as_basic_value_enum(), + AssignOperator::AddAssign => { + env.builder.build_int_add(lhs, rhs, "add_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::SubAssign => { + env.builder.build_int_sub(lhs, rhs, "sub_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::MulAssign => { + env.builder.build_int_mul(lhs, rhs, "mul_assign").unwrap().as_basic_value_enum() + }, AssignOperator::DivAssign if target_unsigned => env .builder .build_int_unsigned_div(lhs, rhs, "div_assign") @@ -409,31 +374,21 @@ pub(crate) fn gen_assign_operation<'ctx, 'a>( }, (BasicValueEnum::FloatValue(lhs), BasicValueEnum::FloatValue(rhs)) => match operator { - AssignOperator::AddAssign => env - .builder - .build_float_add(lhs, rhs, "add_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::SubAssign => env - .builder - .build_float_sub(lhs, rhs, "sub_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::MulAssign => env - .builder - .build_float_mul(lhs, rhs, "mul_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::DivAssign => env - .builder - .build_float_div(lhs, rhs, "div_assign") - .unwrap() - .as_basic_value_enum(), - AssignOperator::RemAssign => env - .builder - .build_float_rem(lhs, rhs, "rem_assign") - .unwrap() - .as_basic_value_enum(), + AssignOperator::AddAssign => { + env.builder.build_float_add(lhs, rhs, "add_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::SubAssign => { + env.builder.build_float_sub(lhs, rhs, "sub_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::MulAssign => { + env.builder.build_float_mul(lhs, rhs, "mul_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::DivAssign => { + env.builder.build_float_div(lhs, rhs, "div_assign").unwrap().as_basic_value_enum() + }, + AssignOperator::RemAssign => { + env.builder.build_float_rem(lhs, rhs, "rem_assign").unwrap().as_basic_value_enum() + }, AssignOperator::Assign => unreachable!(), }, diff --git a/llvm/src/expression/rvalue/async_runtime.rs b/llvm/src/expression/rvalue/async_runtime.rs index fcc648de..6441f9d3 100644 --- a/llvm/src/expression/rvalue/async_runtime.rs +++ b/llvm/src/expression/rvalue/async_runtime.rs @@ -11,10 +11,7 @@ fn runtime<'ctx>( name: &str, args: &[BasicMetadataValueEnum<'ctx>], ) -> BasicValueEnum<'ctx> { - let function = env - .module - .get_function(name) - .expect("async runtime validated before emission"); + let function = env.module.get_function(name).expect("async runtime validated before emission"); env.builder .build_call(function, args, "task") .unwrap() @@ -22,6 +19,7 @@ fn runtime<'ctx>( .basic() .unwrap_or_else(|| env.context.i8_type().const_zero().into()) } + pub(crate) fn gen<'ctx>( env: &mut ExprGenEnv<'ctx, '_>, name: &str, @@ -36,11 +34,9 @@ pub(crate) fn gen<'ctx>( runtime( env, "__wave_task_alloc", - &[i64t - .const_int(env.target_data.get_abi_size(&ty), false) - .into()], + &[i64t.const_int(env.target_data.get_abi_size(&ty), false).into()], ) - } + }, "__wave_async_create" => { let frame = env.gen(&args[0], None); let result = env.gen(&args[1], None); @@ -60,13 +56,12 @@ pub(crate) fn gen<'ctx>( "__wave_task_new", &[ frame.into(), - i64t.const_int(env.target_data.get_abi_size(&ty), false) - .into(), + i64t.const_int(env.target_data.get_abi_size(&ty), false).into(), result.into(), poll.into(), ], ) - } + }, "__wave_async_take" | "__wave_async_block_on" => { let Some(WaveType::Future(result)) = env.wave_type(&args[0]) else { unreachable!("typed future operand") @@ -89,7 +84,7 @@ pub(crate) fn gen<'ctx>( }; runtime(env, "__wave_task_release", &[id.into()]); result - } + }, "__wave_async_interest" | "__wave_async_sleep" => { let values = if name == "__wave_async_sleep" { let ms = env.gen(&args[0], Some(i64t.into())); @@ -105,7 +100,7 @@ pub(crate) fn gen<'ctx>( vec![fd.into(), flags.into(), ms.into()] }; runtime(env, "__wave_task_interest", &values) - } + }, "__wave_async_windows_notify_address" => env .module .get_function("__wave_task_windows_notify") @@ -122,46 +117,34 @@ pub(crate) fn gen<'ctx>( runtime( env, "__wave_task_io", - &[ - fd.into(), - buffer.into(), - length.into(), - writing.into(), - timeout.into(), - ], + &[fd.into(), buffer.into(), length.into(), writing.into(), timeout.into()], ) - } + }, "__wave_async_invoke" => { let callback = env.gen(&args[0], None).into_pointer_value(); let frame = env.gen(&args[1], None); let id = env.gen(&args[2], Some(i64t.into())); - let ty = env.context.bool_type().fn_type( - &[env.context.ptr_type(Default::default()).into(), i64t.into()], - false, - ); + let ty = env + .context + .bool_type() + .fn_type(&[env.context.ptr_type(Default::default()).into(), i64t.into()], false); env.builder .build_indirect_call(ty, callback, &[frame.into(), id.into()], "resume") .unwrap() .try_as_basic_value() .basic() .unwrap() - } + }, "__wave_async_free_slot" => { - let Some(WaveType::Pointer(t)) = env.wave_type(&args[0]) else { - unreachable!() - }; + let Some(WaveType::Pointer(t)) = env.wave_type(&args[0]) else { unreachable!() }; let pointer = env.gen(&args[0], None); let ty = wave_type_to_llvm_type(env.context, &t, env.struct_types, TypeFlavor::Value); runtime( env, "__wave_task_free", - &[ - pointer.into(), - i64t.const_int(env.target_data.get_abi_size(&ty), false) - .into(), - ], + &[pointer.into(), i64t.const_int(env.target_data.get_abi_size(&ty), false).into()], ) - } + }, _ => { let target = match name { "__wave_async_ready" => "__wave_task_ready", @@ -175,10 +158,8 @@ pub(crate) fn gen<'ctx>( "__wave_async_close_fd" => "__wave_task_close_fd", _ => unreachable!("known async intrinsic"), }; - let values = args - .iter() - .map(|a| env.gen(a, Some(i64t.into())).into()) - .collect::>(); + let values = + args.iter().map(|a| env.gen(a, Some(i64t.into())).into()).collect::>(); let result = runtime(env, target, &values); if matches!(name, "__wave_async_ready" | "__wave_async_cancel") { env.builder @@ -193,6 +174,6 @@ pub(crate) fn gen<'ctx>( } else { result } - } + }, } } diff --git a/llvm/src/expression/rvalue/binary.rs b/llvm/src/expression/rvalue/binary.rs index e6277a94..1962c12e 100644 --- a/llvm/src/expression/rvalue/binary.rs +++ b/llvm/src/expression/rvalue/binary.rs @@ -24,11 +24,7 @@ use inkwell::IntPredicate; use parser::ast::{Expression, Operator, WaveType}; fn pointer_index(value: IntValue<'_>) -> IntValue<'_> { - assert_eq!( - value.get_type().get_bit_width(), - 64, - "ICE: pointer index missing HIR conversion" - ); + assert_eq!(value.get_type().get_bit_width(), 64, "ICE: pointer index missing HIR conversion"); value } @@ -39,7 +35,7 @@ fn infer_ptr_pointee_ty<'ctx, 'a>( match env.wave_type(expr) { Some(WaveType::Pointer(inner)) => { wave_type_to_llvm_type(env.context, &inner, env.struct_types, TypeFlavor::Value) - } + }, Some(WaveType::String) => env.context.i8_type().as_basic_type_enum(), other => panic!("typed pointer arithmetic requires a pointee type, found {other:?}"), } @@ -56,11 +52,7 @@ fn gep_with_i64_offset<'ctx, 'a>( // SAFETY: Wave pointer arithmetic is explicitly unchecked. A source program // must keep an inbounds result within the original allocation (or one past // it), which is the contract required by LLVM's `inbounds` GEP. - unsafe { - env.builder - .build_in_bounds_gep(pointee_ty, ptr, &[idx_i64], tag) - .unwrap() - } + unsafe { env.builder.build_in_bounds_gep(pointee_ty, ptr, &[idx_i64], tag).unwrap() } } pub(crate) fn gen<'ctx, 'a>( @@ -79,13 +71,9 @@ pub(crate) fn gen<'ctx, 'a>( let merge_block = env.context.append_basic_block(function, "logical.end"); if matches!(operator, Operator::LogicalAnd) { - env.builder - .build_conditional_branch(left_bool, right_block, merge_block) - .unwrap(); + env.builder.build_conditional_branch(left_bool, right_block, merge_block).unwrap(); } else { - env.builder - .build_conditional_branch(left_bool, merge_block, right_block) - .unwrap(); + env.builder.build_conditional_branch(left_bool, merge_block, right_block).unwrap(); } env.builder.position_at_end(right_block); @@ -95,27 +83,17 @@ pub(crate) fn gen<'ctx, 'a>( env.builder.build_unconditional_branch(merge_block).unwrap(); env.builder.position_at_end(merge_block); - let short_value = env.context.bool_type().const_int( - if matches!(operator, Operator::LogicalOr) { - 1 - } else { - 0 - }, - false, - ); - let phi = env - .builder - .build_phi(env.context.bool_type(), "logical.result") - .unwrap(); + let short_value = env + .context + .bool_type() + .const_int(if matches!(operator, Operator::LogicalOr) { 1 } else { 0 }, false); + let phi = env.builder.build_phi(env.context.bool_type(), "logical.result").unwrap(); phi.add_incoming(&[(&short_value, left_block), (&right_bool, right_end)]); let mut result = phi.as_basic_value().into_int_value(); if let Some(BasicTypeEnum::IntType(expected)) = expected_type { if result.get_type() != expected { - result = env - .builder - .build_int_z_extend(result, expected, "logical.cast") - .unwrap(); + result = env.builder.build_int_z_extend(result, expected, "logical.cast").unwrap(); } } @@ -127,24 +105,16 @@ pub(crate) fn gen<'ctx, 'a>( match (left_val, right_val) { (BasicValueEnum::PointerValue(lp), BasicValueEnum::PointerValue(rp)) => { let i64_ty = env.context.i64_type(); - let li = env - .builder - .build_ptr_to_int(lp, i64_ty, "l_ptr2int") - .unwrap(); - let ri = env - .builder - .build_ptr_to_int(rp, i64_ty, "r_ptr2int") - .unwrap(); + let li = env.builder.build_ptr_to_int(lp, i64_ty, "l_ptr2int").unwrap(); + let ri = env.builder.build_ptr_to_int(rp, i64_ty, "r_ptr2int").unwrap(); let mut result = match operator { - Operator::Equal => env - .builder - .build_int_compare(IntPredicate::EQ, li, ri, "ptreq") - .unwrap(), - Operator::NotEqual => env - .builder - .build_int_compare(IntPredicate::NE, li, ri, "ptrne") - .unwrap(), + Operator::Equal => { + env.builder.build_int_compare(IntPredicate::EQ, li, ri, "ptreq").unwrap() + }, + Operator::NotEqual => { + env.builder.build_int_compare(IntPredicate::NE, li, ri, "ptrne").unwrap() + }, Operator::Subtract => env.builder.build_int_sub(li, ri, "ptrdiff").unwrap(), _ => panic!("Unsupported pointer operator: {:?}", operator), }; @@ -166,7 +136,7 @@ pub(crate) fn gen<'ctx, 'a>( .unwrap(); } } - } + }, Operator::Subtract => { if let Some(inkwell::types::BasicTypeEnum::IntType(target_ty)) = expected_type { if result.get_type() != target_ty { @@ -176,12 +146,12 @@ pub(crate) fn gen<'ctx, 'a>( .unwrap(); } } - } - _ => {} + }, + _ => {}, } return result.as_basic_value_enum(); - } + }, (BasicValueEnum::PointerValue(lp), BasicValueEnum::IntValue(ri)) => { match operator { @@ -192,27 +162,22 @@ pub(crate) fn gen<'ctx, 'a>( } let p = gep_with_i64_offset(env, lp, left, idx, "ptr_gep"); return p.as_basic_value_enum(); - } - _ => {} + }, + _ => {}, }; let i64_ty = env.context.i64_type(); - let li = env - .builder - .build_ptr_to_int(lp, i64_ty, "l_ptr2int") - .unwrap(); + let li = env.builder.build_ptr_to_int(lp, i64_ty, "l_ptr2int").unwrap(); let ri = pointer_index(ri); let mut result = match operator { - Operator::Equal => env - .builder - .build_int_compare(IntPredicate::EQ, li, ri, "ptreq0") - .unwrap(), - Operator::NotEqual => env - .builder - .build_int_compare(IntPredicate::NE, li, ri, "ptrne0") - .unwrap(), + Operator::Equal => { + env.builder.build_int_compare(IntPredicate::EQ, li, ri, "ptreq0").unwrap() + }, + Operator::NotEqual => { + env.builder.build_int_compare(IntPredicate::NE, li, ri, "ptrne0").unwrap() + }, _ => panic!("Unsupported ptr/int operator: {:?}", operator), }; @@ -225,15 +190,12 @@ pub(crate) fn gen<'ctx, 'a>( target_ty.get_bit_width() ); } - result = env - .builder - .build_int_cast(result, target_ty, "cast_result") - .unwrap(); + result = env.builder.build_int_cast(result, target_ty, "cast_result").unwrap(); } } return result.as_basic_value_enum(); - } + }, (BasicValueEnum::IntValue(li), BasicValueEnum::PointerValue(rp)) => { if matches!(operator, Operator::Add) { @@ -245,20 +207,15 @@ pub(crate) fn gen<'ctx, 'a>( let i64_ty = env.context.i64_type(); let li = pointer_index(li); - let ri = env - .builder - .build_ptr_to_int(rp, i64_ty, "r_ptr2int") - .unwrap(); + let ri = env.builder.build_ptr_to_int(rp, i64_ty, "r_ptr2int").unwrap(); let mut result = match operator { - Operator::Equal => env - .builder - .build_int_compare(IntPredicate::EQ, li, ri, "ptreq0") - .unwrap(), - Operator::NotEqual => env - .builder - .build_int_compare(IntPredicate::NE, li, ri, "ptrne0") - .unwrap(), + Operator::Equal => { + env.builder.build_int_compare(IntPredicate::EQ, li, ri, "ptreq0").unwrap() + }, + Operator::NotEqual => { + env.builder.build_int_compare(IntPredicate::NE, li, ri, "ptrne0").unwrap() + }, _ => panic!("Unsupported int/ptr operator: {:?}", operator), }; @@ -271,15 +228,12 @@ pub(crate) fn gen<'ctx, 'a>( target_ty.get_bit_width() ); } - result = env - .builder - .build_int_cast(result, target_ty, "cast_result") - .unwrap(); + result = env.builder.build_int_cast(result, target_ty, "cast_result").unwrap(); } } return result.as_basic_value_enum(); - } + }, _ => panic!("Type mismatch in binary expression"), } diff --git a/llvm/src/expression/rvalue/calls.rs b/llvm/src/expression/rvalue/calls.rs index e5b5025f..2d2e0ba8 100644 --- a/llvm/src/expression/rvalue/calls.rs +++ b/llvm/src/expression/rvalue/calls.rs @@ -67,7 +67,7 @@ fn callsite_to_ret<'ctx>( panic!("Expected a return value from {}", what); } None - } + }, } } @@ -79,16 +79,10 @@ fn pack_agg_to_int<'ctx, 'a>( ) -> BasicValueEnum<'ctx> { let agg_ty = agg.get_type(); - let agg_tmp = env - .builder - .build_alloca(agg_ty, &format!("{}_agg_tmp", tag)) - .unwrap(); + let agg_tmp = env.builder.build_alloca(agg_ty, &format!("{}_agg_tmp", tag)).unwrap(); env.builder.build_store(agg_tmp, agg).unwrap(); - let int_tmp = env - .builder - .build_alloca(dst, &format!("{}_int_tmp", tag)) - .unwrap(); + let int_tmp = env.builder.build_alloca(dst, &format!("{}_int_tmp", tag)).unwrap(); // The ABI transport slot may be wider than the aggregate object (for // example a 3-byte RV64 aggregate transported in i64). Keep padding bits @@ -98,14 +92,9 @@ fn pack_agg_to_int<'ctx, 'a>( let bytes = env.target_data.get_store_size(&agg_ty) as u64; let size_v = env.context.i64_type().const_int(bytes, false); - env.builder - .build_memcpy(int_tmp, 1, agg_tmp, 1, size_v) - .unwrap(); + env.builder.build_memcpy(int_tmp, 1, agg_tmp, 1, size_v).unwrap(); - env.builder - .build_load(dst, int_tmp, &format!("{}_agg_i", tag)) - .unwrap() - .as_basic_value_enum() + env.builder.build_load(dst, int_tmp, &format!("{}_agg_i", tag)).unwrap().as_basic_value_enum() } fn unpack_int_to_agg<'ctx, 'a>( @@ -114,23 +103,15 @@ fn unpack_int_to_agg<'ctx, 'a>( dst_agg_ty: BasicTypeEnum<'ctx>, tag: &str, ) -> BasicValueEnum<'ctx> { - let agg_tmp = env - .builder - .build_alloca(dst_agg_ty, &format!("{}_agg_tmp", tag)) - .unwrap(); - let int_tmp = env - .builder - .build_alloca(iv.get_type(), &format!("{}_int_tmp", tag)) - .unwrap(); + let agg_tmp = env.builder.build_alloca(dst_agg_ty, &format!("{}_agg_tmp", tag)).unwrap(); + let int_tmp = env.builder.build_alloca(iv.get_type(), &format!("{}_int_tmp", tag)).unwrap(); env.builder.build_store(int_tmp, iv).unwrap(); let bytes = env.target_data.get_store_size(&dst_agg_ty) as u64; let size_v = env.context.i64_type().const_int(bytes, false); - env.builder - .build_memcpy(agg_tmp, 1, int_tmp, 1, size_v) - .unwrap(); + env.builder.build_memcpy(agg_tmp, 1, int_tmp, 1, size_v).unwrap(); env.builder .build_load(dst_agg_ty, agg_tmp, &format!("{}_i2agg_load", tag)) @@ -151,11 +132,7 @@ fn lower_c_variadic_argument<'ctx, 'a>( match value { BasicValueEnum::FloatValue(float) if float.get_type() == env.context.f32_type() => env .builder - .build_float_ext( - float, - env.context.f64_type(), - &format!("vararg{}_f64", index), - ) + .build_float_ext(float, env.context.f64_type(), &format!("vararg{}_f64", index)) .unwrap() .as_basic_value_enum(), BasicValueEnum::IntValue(integer) if integer.get_type().get_bit_width() < 32 => { @@ -172,7 +149,7 @@ fn lower_c_variadic_argument<'ctx, 'a>( .unwrap() .as_basic_value_enum() } - } + }, BasicValueEnum::IntValue(_) | BasicValueEnum::FloatValue(_) | BasicValueEnum::PointerValue(_) => value, @@ -197,10 +174,8 @@ pub(crate) fn gen_method_call<'ctx, 'a>( }, _ => None, }; - let method = owner.and_then(|owner| { - env.module - .get_function(&parser::ast::method_symbol(owner, name)) - }); + let method = + owner.and_then(|owner| env.module.get_function(&parser::ast::method_symbol(owner, name))); let function = method .or_else(|| env.module.get_function(name)) .expect("validated method must have a lowered function"); @@ -223,16 +198,9 @@ pub(crate) fn gen_method_call<'ctx, 'a>( }; call_args.push(value.into()); } - let call = env - .builder - .build_call(function, &call_args, "method_call") - .unwrap(); - callsite_to_ret( - call, - function.get_type().get_return_type().is_some(), - "method call", - ) - .unwrap_or_else(|| env.context.i32_type().const_zero().as_basic_value_enum()) + let call = env.builder.build_call(function, &call_args, "method_call").unwrap(); + callsite_to_ret(call, function.get_type().get_return_type().is_some(), "method call") + .unwrap_or_else(|| env.context.i32_type().const_zero().as_basic_value_enum()) } pub(crate) fn gen_function_call<'ctx, 'a>( @@ -260,10 +228,7 @@ pub(crate) fn gen_function_call<'ctx, 'a>( return super::async_runtime::gen(env, name, type_args, args); } if !type_args.is_empty() { - panic!( - "generic call '{}<...>(...)' reached codegen without monomorphization", - name - ); + panic!("generic call '{}<...>(...)' reached codegen without monomorphization", name); } if env.extern_c_info.contains_key(name) { @@ -279,26 +244,16 @@ fn gen_wave_call<'ctx, 'a>( args: &[Expression], expected_type: Option>, ) -> BasicValueEnum<'ctx> { - let function = env - .module - .get_function(name) - .unwrap_or_else(|| panic!("Function '{}' not found", name)); + let function = + env.module.get_function(name).unwrap_or_else(|| panic!("Function '{}' not found", name)); let fn_type = function.get_type(); - let param_types: Vec> = fn_type - .get_param_types() - .into_iter() - .map(meta_to_basic) - .collect(); + let param_types: Vec> = + fn_type.get_param_types().into_iter().map(meta_to_basic).collect(); let ret_type: Option = fn_type.get_return_type(); if args.len() != param_types.len() { - panic!( - "Function `{}` expects {} arguments, got {}", - name, - param_types.len(), - args.len() - ); + panic!("Function `{}` expects {} arguments, got {}", name, param_types.len(), args.len()); } let mut call_args: Vec = Vec::with_capacity(args.len()); @@ -311,28 +266,18 @@ fn gen_wave_call<'ctx, 'a>( call_args.push(val.into()); } - let call_name = if ret_type.is_some() { - format!("call_{}", name) - } else { - String::new() - }; + let call_name = if ret_type.is_some() { format!("call_{}", name) } else { String::new() }; - let call_site = env - .builder - .build_call(function, &call_args, &call_name) - .unwrap(); + let call_site = env.builder.build_call(function, &call_args, &call_name).unwrap(); match ret_type { Some(_) => callsite_to_ret(call_site, true, "function call").unwrap(), None => { if expected_type.is_some() { - panic!( - "Function '{}' returns void and cannot be used as a value", - name - ); + panic!("Function '{}' returns void and cannot be used as a value", name); } env.context.i32_type().const_zero().as_basic_value_enum() - } + }, } } @@ -345,10 +290,7 @@ fn gen_extern_call<'ctx, 'a>( let info = env.extern_c_info.get(name).expect("extern ABI was checked"); let function = env.module.get_function(&info.llvm_name).unwrap_or_else(|| { - panic!( - "Extern function '{}' not found in module (symbol alias?)", - name - ) + panic!("Extern function '{}' not found in module (symbol alias?)", name) }); if (!info.variadic && args.len() != info.params.len()) @@ -374,10 +316,7 @@ fn gen_extern_call<'ctx, 'a>( let mut sret_tmp: Option> = None; if let RetLowering::SRet { ty, .. } = &info.ret { let agg = any_agg_to_basic(*ty); - let tmp = env - .builder - .build_alloca(agg, &format!("{}_sret_tmp", name)) - .unwrap(); + let tmp = env.builder.build_alloca(agg, &format!("{}_sret_tmp", name)).unwrap(); let expected_ptr = meta_into_ptr(llvm_param_types[0]); let tmp2 = coerce_ptr_to(env, tmp, expected_ptr, &format!("{}_sret_ptrcast", name)); @@ -392,22 +331,20 @@ fn gen_extern_call<'ctx, 'a>( match p { ParamLowering::Ignore => { env.gen(arg_expr, None); - } + }, ParamLowering::Direct(t) => { let source_unsigned = wave_type_is_unsigned(env.wave_type(arg_expr).as_ref()); let mut v = env.gen(arg_expr, Some(*t)); v = coerce_to_expected(env, v, *t, name, i, source_unsigned); lowered_args.push(v.into()); llvm_pi += 1; - } + }, ParamLowering::Indirect { ty } | ParamLowering::ByVal { ty, .. } => { let agg = any_agg_to_basic(*ty); let v = env.gen(arg_expr, Some(agg)); - let tmp = env - .builder - .build_alloca(agg, &format!("{}_byval_tmp_{}", name, i)) - .unwrap(); + let tmp = + env.builder.build_alloca(agg, &format!("{}_byval_tmp_{}", name, i)).unwrap(); // Windows x64 passes odd-sized aggregates through a caller // temporary aligned to at least 16 bytes, without byval. if matches!( @@ -422,15 +359,11 @@ fn gen_extern_call<'ctx, 'a>( env.builder.build_store(tmp, v).unwrap(); let expected_ptr = meta_into_ptr(llvm_param_types[llvm_pi]); - let tmp2 = coerce_ptr_to( - env, - tmp, - expected_ptr, - &format!("{}_byval_ptrcast_{}", name, i), - ); + let tmp2 = + coerce_ptr_to(env, tmp, expected_ptr, &format!("{}_byval_ptrcast_{}", name, i)); lowered_args.push(tmp2.as_basic_value_enum().into()); llvm_pi += 1; - } + }, ParamLowering::Split(parts) => { let agg_val = env.gen(arg_expr, None); @@ -451,7 +384,7 @@ fn gen_extern_call<'ctx, 'a>( lowered_args.push(vv.into()); llvm_pi += 1; } - } + }, ParamLowering::CoerceAndExpand(parts) => { let aggregate = env.gen(arg_expr, None); for value in coerce_and_expand_agg_parts( @@ -463,7 +396,7 @@ fn gen_extern_call<'ctx, 'a>( lowered_args.push(value.into()); llvm_pi += 1; } - } + }, } } @@ -478,10 +411,7 @@ fn gen_extern_call<'ctx, 'a>( _ => format!("call_{}", name), }; - let call_site = env - .builder - .build_call(function, &lowered_args, &call_name) - .unwrap(); + let call_site = env.builder.build_call(function, &lowered_args, &call_name).unwrap(); apply_extern_c_callsite_attrs(env.context, call_site, info); apply_extern_c_variadic_callsite_attrs(env.context, call_site, info, &variadic_args); @@ -495,21 +425,15 @@ fn gen_extern_call<'ctx, 'a>( ); } if expected_type.is_some() { - panic!( - "Extern '{}' returns void and cannot be used as a value", - name - ); + panic!("Extern '{}' returns void and cannot be used as a value", name); } return env.context.i32_type().const_zero().as_basic_value_enum(); - } + }, RetLowering::SRet { ty, .. } => { let tmp = sret_tmp.expect("SRet lowering requires sret tmp"); let agg = any_agg_to_basic(*ty); - let v = env - .builder - .build_load(agg, tmp, &format!("{}_sret_load", name)) - .unwrap(); + let v = env.builder.build_load(agg, tmp, &format!("{}_sret_load", name)).unwrap(); if let Some(et) = expected_type { return coerce_lowered_ret_to_expected( @@ -520,7 +444,7 @@ fn gen_extern_call<'ctx, 'a>( ); } return v.as_basic_value_enum(); - } + }, RetLowering::Direct(_t) => { let rv = callsite_to_ret(call_site, true, "extern direct ret").unwrap(); @@ -529,7 +453,7 @@ fn gen_extern_call<'ctx, 'a>( return coerce_lowered_ret_to_expected(env, rv, et, "direct_ret"); } return rv; - } + }, } } @@ -556,7 +480,7 @@ fn coerce_to_expected<'ctx, 'a>( .build_ptr_to_int(pv, dst, &format!("arg{}_p2i", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 0.1) int -> ptr (inttoptr) (useful when passing raw addresses) (BasicTypeEnum::IntType(src), BasicTypeEnum::PointerType(dst)) @@ -567,17 +491,14 @@ fn coerce_to_expected<'ctx, 'a>( .build_int_to_ptr(iv, dst, &format!("arg{}_i2p", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // The only scalar adaptation left here is bool's C storage width. (BasicTypeEnum::IntType(src), BasicTypeEnum::IntType(dst)) if src.get_bit_width() == 1 && dst.get_bit_width() == 8 => { - env.builder - .build_int_z_extend(val.into_int_value(), dst, "bool.abi") - .unwrap() - .into() - } + env.builder.build_int_z_extend(val.into_int_value(), dst, "bool.abi").unwrap().into() + }, // 2) ptr -> array value (load) (BasicTypeEnum::PointerType(_), BasicTypeEnum::ArrayType(a)) => { @@ -586,7 +507,7 @@ fn coerce_to_expected<'ctx, 'a>( .build_load(a, ptr, &format!("arg{}_arr_load", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 3) ptr -> struct value (load) (BasicTypeEnum::PointerType(_), BasicTypeEnum::StructType(s)) => { @@ -595,7 +516,7 @@ fn coerce_to_expected<'ctx, 'a>( .build_load(s, ptr, &format!("arg{}_st_load", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 3.1) a single-pointer aggregate transported as the pointer value // itself by x86_64 SysV and AArch64 argument lowering. @@ -603,16 +524,14 @@ fn coerce_to_expected<'ctx, 'a>( got_agg @ (BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)), BasicTypeEnum::PointerType(dst), ) => { - let source = env - .builder - .build_alloca(got_agg, &format!("arg{}_ptr_agg", arg_index)) - .unwrap(); + let source = + env.builder.build_alloca(got_agg, &format!("arg{}_ptr_agg", arg_index)).unwrap(); env.builder.build_store(source, val).unwrap(); env.builder .build_load(dst, source, &format!("arg{}_ptr_transport", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 4) ptr -> ptr (bitcast) (BasicTypeEnum::PointerType(_), BasicTypeEnum::PointerType(dst)) => env @@ -640,7 +559,7 @@ fn coerce_to_expected<'ctx, 'a>( arg_index, name ); - } + }, // 4.45) object aggregate -> aggregate-shaped ABI transport slot. // AArch64 and RV64 represent 9..16-byte integer aggregates as a @@ -659,18 +578,14 @@ fn coerce_to_expected<'ctx, 'a>( ); } - let source = env - .builder - .build_alloca(got_agg, &format!("arg{}_agg_source", arg_index)) - .unwrap(); + let source = + env.builder.build_alloca(got_agg, &format!("arg{}_agg_source", arg_index)).unwrap(); env.builder.build_store(source, val).unwrap(); let transport = env .builder .build_alloca(dst_agg, &format!("arg{}_agg_transport", arg_index)) .unwrap(); - env.builder - .build_store(transport, dst_agg.const_zero()) - .unwrap(); + env.builder.build_store(transport, dst_agg.const_zero()).unwrap(); let bytes = env.context.i64_type().const_int(got_size, false); env.builder .build_memcpy( @@ -682,14 +597,10 @@ fn coerce_to_expected<'ctx, 'a>( ) .unwrap(); env.builder - .build_load( - dst_agg, - transport, - &format!("arg{}_agg_transport_load", arg_index), - ) + .build_load(dst_agg, transport, &format!("arg{}_agg_transport_load", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 4.5) agg(struct/array) -> vector (HFA/ABI: e.g. Vector2 passed as <2 x float>) ( @@ -698,9 +609,7 @@ fn coerce_to_expected<'ctx, 'a>( ) => { // size check (ABI layout must match) let got_sz = env.target_data.get_store_size(&got_agg); - let exp_sz = env - .target_data - .get_store_size(&BasicTypeEnum::VectorType(vt)); + let exp_sz = env.target_data.get_store_size(&BasicTypeEnum::VectorType(vt)); if got_sz != exp_sz { panic!( "Cannot coerce agg->vector: size mismatch {} vs {} (arg {} of {})", @@ -708,26 +617,22 @@ fn coerce_to_expected<'ctx, 'a>( ); } - let tmp = env - .builder - .build_alloca(got_agg, &format!("arg{}_agg_tmp", arg_index)) - .unwrap(); + let tmp = + env.builder.build_alloca(got_agg, &format!("arg{}_agg_tmp", arg_index)).unwrap(); env.builder.build_store(tmp, val).unwrap(); env.builder .build_load(vt, tmp, &format!("arg{}_agg2v", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 4.6) vector -> agg(struct/array) (reverse of above) ( BasicTypeEnum::VectorType(vt), dst_agg @ (BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)), ) => { - let got_sz = env - .target_data - .get_store_size(&BasicTypeEnum::VectorType(vt)); + let got_sz = env.target_data.get_store_size(&BasicTypeEnum::VectorType(vt)); let exp_sz = env.target_data.get_store_size(&dst_agg); if got_sz != exp_sz { panic!( @@ -736,10 +641,8 @@ fn coerce_to_expected<'ctx, 'a>( ); } - let tmp = env - .builder - .build_alloca(dst_agg, &format!("arg{}_v2agg_tmp", arg_index)) - .unwrap(); + let tmp = + env.builder.build_alloca(dst_agg, &format!("arg{}_v2agg_tmp", arg_index)).unwrap(); env.builder.build_store(tmp, val).unwrap(); @@ -747,7 +650,7 @@ fn coerce_to_expected<'ctx, 'a>( .build_load(dst_agg, tmp, &format!("arg{}_v2agg", arg_index)) .unwrap() .as_basic_value_enum() - } + }, // 4.7) ptr -> vector (used for ptr-to-agg cases) (BasicTypeEnum::PointerType(_), BasicTypeEnum::VectorType(vt)) => { @@ -756,7 +659,7 @@ fn coerce_to_expected<'ctx, 'a>( .build_load(vt, pv, &format!("arg{}_p2v", arg_index)) .unwrap() .as_basic_value_enum() - } + }, ( BasicTypeEnum::IntType(src), @@ -775,14 +678,14 @@ fn coerce_to_expected<'ctx, 'a>( arg_index, name ); - } + }, _ => { panic!( "Type mismatch for arg {} of '{}': expected {:?}, got {:?}", arg_index, name, expected, got ); - } + }, } } @@ -790,10 +693,7 @@ fn any_agg_to_basic<'ctx>(ty: AnyTypeEnum<'ctx>) -> BasicTypeEnum<'ctx> { match ty { AnyTypeEnum::StructType(st) => st.as_basic_type_enum(), AnyTypeEnum::ArrayType(at) => at.as_basic_type_enum(), - _ => panic!( - "Expected aggregate AnyTypeEnum (struct/array), got {:?}", - ty - ), + _ => panic!("Expected aggregate AnyTypeEnum (struct/array), got {:?}", ty), } } @@ -807,11 +707,7 @@ fn coerce_ptr_to<'ctx, 'a>( return pv; } env.builder - .build_bit_cast( - pv.as_basic_value_enum(), - expected_ptr_ty.as_basic_type_enum(), - tag, - ) + .build_bit_cast(pv.as_basic_value_enum(), expected_ptr_ty.as_basic_type_enum(), tag) .unwrap() .into_pointer_value() } @@ -823,10 +719,7 @@ fn split_agg_parts_from_agg<'ctx, 'a>( tag: &str, ) -> Vec> { let mut out = Vec::with_capacity(parts.len()); - let total_bytes: u64 = parts - .iter() - .map(|t| env.target_data.get_store_size(t) as u64) - .sum(); + let total_bytes: u64 = parts.iter().map(|t| env.target_data.get_store_size(t) as u64).sum(); if total_bytes == 0 { panic!("Split lowering got zero-sized parts"); } @@ -834,18 +727,10 @@ fn split_agg_parts_from_agg<'ctx, 'a>( let aggregate_type = agg_val.get_type(); let aggregate_size = env.target_data.get_store_size(&aggregate_type); let staging_type = env.context.i8_type().array_type(total_bytes as u32); - let staging = env - .builder - .build_alloca(staging_type, &format!("{tag}_staging")) - .unwrap(); - env.builder - .build_store(staging, staging_type.const_zero()) - .unwrap(); + let staging = env.builder.build_alloca(staging_type, &format!("{tag}_staging")).unwrap(); + env.builder.build_store(staging, staging_type.const_zero()).unwrap(); - let aggregate = env - .builder - .build_alloca(aggregate_type, &format!("{tag}_aggregate")) - .unwrap(); + let aggregate = env.builder.build_alloca(aggregate_type, &format!("{tag}_aggregate")).unwrap(); env.builder.build_store(aggregate, agg_val).unwrap(); env.builder .build_memcpy( @@ -853,29 +738,19 @@ fn split_agg_parts_from_agg<'ctx, 'a>( 1, aggregate, env.target_data.get_abi_alignment(&aggregate_type), - env.context - .i64_type() - .const_int(aggregate_size.min(total_bytes), false), + env.context.i64_type().const_int(aggregate_size.min(total_bytes), false), ) .unwrap(); let mut offset: u64 = 0; for (pi, part_ty) in parts.iter().enumerate() { let part_size = env.target_data.get_store_size(part_ty) as u64; - let dst = env - .builder - .build_alloca(*part_ty, &format!("{tag}_part_dst_{pi}")) - .unwrap(); + let dst = env.builder.build_alloca(*part_ty, &format!("{tag}_part_dst_{pi}")).unwrap(); let off = env.context.i64_type().const_int(offset, false); let src_off = unsafe { env.builder - .build_gep( - env.context.i8_type(), - staging, - &[off], - &format!("{tag}_src_gep_{pi}"), - ) + .build_gep(env.context.i8_type(), staging, &[off], &format!("{tag}_src_gep_{pi}")) .unwrap() }; @@ -902,10 +777,7 @@ fn coerce_and_expand_agg_parts<'ctx, 'a>( ) -> Vec> { let aggregate_type = aggregate.get_type(); let aggregate_size = env.target_data.get_store_size(&aggregate_type); - let source = env - .builder - .build_alloca(aggregate_type, &format!("{tag}_aggregate")) - .unwrap(); + let source = env.builder.build_alloca(aggregate_type, &format!("{tag}_aggregate")).unwrap(); env.builder.build_store(source, aggregate).unwrap(); parts @@ -917,10 +789,8 @@ fn coerce_and_expand_agg_parts<'ctx, 'a>( part.offset + size <= aggregate_size, "LoongArch ABI part extends past its aggregate" ); - let destination = env - .builder - .build_alloca(part.ty, &format!("{tag}_part_{index}")) - .unwrap(); + let destination = + env.builder.build_alloca(part.ty, &format!("{tag}_part_{index}")).unwrap(); let offset = env.context.i64_type().const_int(part.offset, false); // SAFETY: classification derives every offset from the target data // layout and verifies the complete part remains in the allocation. @@ -966,51 +836,43 @@ fn coerce_lowered_ret_to_expected<'ctx, 'a>( BasicTypeEnum::PointerType(_), BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_), ) => { - let destination = env - .builder - .build_alloca(expected, &format!("{tag}_ptr_agg")) - .unwrap(); + let destination = + env.builder.build_alloca(expected, &format!("{tag}_ptr_agg")).unwrap(); env.builder.build_store(destination, lowered_ret).unwrap(); env.builder .build_load(expected, destination, &format!("{tag}_ptr_agg_load")) .unwrap() .as_basic_value_enum() - } + }, (BasicTypeEnum::IntType(_), BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_)) => { let iv = lowered_ret.into_int_value(); unpack_int_to_agg(env, iv, expected, tag) - } + }, // vector(float) -> struct/array (HFA ret) ( BasicTypeEnum::VectorType(_vt), BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_), ) => { - let tmp = env - .builder - .build_alloca(expected, &format!("{tag}_v2agg_tmp")) - .unwrap(); + let tmp = env.builder.build_alloca(expected, &format!("{tag}_v2agg_tmp")).unwrap(); env.builder.build_store(tmp, lowered_ret).unwrap(); env.builder .build_load(expected, tmp, &format!("{tag}_v2agg_load")) .unwrap() .as_basic_value_enum() - } + }, ( BasicTypeEnum::FloatType(_ft), BasicTypeEnum::StructType(_) | BasicTypeEnum::ArrayType(_), ) => { - let tmp = env - .builder - .build_alloca(expected, &format!("{tag}_f2agg_tmp")) - .unwrap(); + let tmp = env.builder.build_alloca(expected, &format!("{tag}_f2agg_tmp")).unwrap(); env.builder.build_store(tmp, lowered_ret).unwrap(); env.builder .build_load(expected, tmp, &format!("{tag}_f2agg_load")) .unwrap() .as_basic_value_enum() - } + }, // lowered tuple-struct (e.g. {double,double}, {<2xf>,float}) -> expected aggregate ( @@ -1025,16 +887,10 @@ fn coerce_lowered_ret_to_expected<'ctx, 'a>( return lowered_ret; } - let src = env - .builder - .build_alloca(got, &format!("{tag}_agg_src")) - .unwrap(); + let src = env.builder.build_alloca(got, &format!("{tag}_agg_src")).unwrap(); env.builder.build_store(src, lowered_ret).unwrap(); - let dst = env - .builder - .build_alloca(expected, &format!("{tag}_agg_dst")) - .unwrap(); + let dst = env.builder.build_alloca(expected, &format!("{tag}_agg_dst")).unwrap(); let bytes = env.context.i64_type().const_int(exp_sz as u64, false); env.builder.build_memcpy(dst, 1, src, 1, bytes).unwrap(); @@ -1043,7 +899,7 @@ fn coerce_lowered_ret_to_expected<'ctx, 'a>( .build_load(expected, dst, &format!("{tag}_agg_cast")) .unwrap() .as_basic_value_enum() - } + }, _ => lowered_ret, } diff --git a/llvm/src/expression/rvalue/const_projection.rs b/llvm/src/expression/rvalue/const_projection.rs index e40078d8..ffdd5578 100644 --- a/llvm/src/expression/rvalue/const_projection.rs +++ b/llvm/src/expression/rvalue/const_projection.rs @@ -26,7 +26,7 @@ fn is_rooted_in_const(env: &ExprGenEnv<'_, '_>, expression: &Expression) -> bool match expression { Expression::Variable(name) => { !env.variables.contains_key(name) && env.global_consts.contains_key(name) - } + }, Expression::Grouped(inner) => is_rooted_in_const(env, inner), Expression::FieldAccess { object, .. } => is_rooted_in_const(env, object), Expression::IndexAccess { target, .. } => is_rooted_in_const(env, target), @@ -35,9 +35,7 @@ fn is_rooted_in_const(env: &ExprGenEnv<'_, '_>, expression: &Expression) -> bool } fn normalize_struct_name(raw: &str) -> &str { - raw.strip_prefix("struct.") - .unwrap_or(raw) - .trim_start_matches('%') + raw.strip_prefix("struct.").unwrap_or(raw).trim_start_matches('%') } pub(crate) fn try_gen_field_access<'ctx, 'a>( @@ -70,17 +68,15 @@ pub(crate) fn try_gen_field_access<'ctx, 'a>( ) }); - Some( - env.builder - .build_extract_value(value, field_index, "const.field") - .unwrap_or_else(|error| { - panic!( - "typed HIR produced an invalid constant field projection '{}.{}': {}", - struct_name, field, error - ) - }), - ) - } + Some(env.builder.build_extract_value(value, field_index, "const.field").unwrap_or_else( + |error| { + panic!( + "typed HIR produced an invalid constant field projection '{}.{}': {}", + struct_name, field, error + ) + }, + )) + }, BasicValueEnum::PointerValue(pointer) => { let WaveType::Pointer(pointee) = env.wave_type(object).unwrap_or_else(|| { panic!("typed HIR omitted the type of constant pointer field access") @@ -114,7 +110,7 @@ pub(crate) fn try_gen_field_access<'ctx, 'a>( .unwrap() .as_basic_value_enum(), ) - } + }, other => panic!( "typed HIR allowed field access on a non-aggregate constant value: {:?}", other.get_type() @@ -150,10 +146,7 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( // LLVM extractvalue requires a fixed immediate index. Keep dynamic // indexing value-only by materializing an internal temporary whose // address is never entered in the Wave variable environment. - let storage = env - .builder - .build_alloca(array_type, "const.array.storage") - .unwrap(); + let storage = env.builder.build_alloca(array_type, "const.array.storage").unwrap(); env.builder.build_store(storage, array).unwrap(); let zero = env.context.i32_type().const_zero(); let element_pointer = unsafe { @@ -176,7 +169,7 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( .unwrap() .as_basic_value_enum(), ) - } + }, BasicValueEnum::PointerValue(pointer) => { let target_type = env.wave_type(target).unwrap_or_else(|| { panic!("typed HIR omitted the type of constant pointer index access") @@ -203,7 +196,7 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( .unwrap() }; (array_type.get_element_type(), element_pointer) - } + }, element => { let element_type = wave_type_to_llvm_type( env.context, @@ -222,7 +215,7 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( .unwrap() }; (element_type, element_pointer) - } + }, }, WaveType::String => { let element_type = env.context.i8_type().as_basic_type_enum(); @@ -237,11 +230,8 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( .unwrap() }; (element_type, element_pointer) - } - other => panic!( - "constant pointer index access has unsupported type {:?}", - other - ), + }, + other => panic!("constant pointer index access has unsupported type {:?}", other), }; Some( @@ -250,7 +240,7 @@ pub(crate) fn try_gen_index_access<'ctx, 'a>( .unwrap() .as_basic_value_enum(), ) - } + }, other => panic!( "typed HIR allowed index access on a non-indexable constant value: {:?}", other.get_type() diff --git a/llvm/src/expression/rvalue/dispatch.rs b/llvm/src/expression/rvalue/dispatch.rs index a08bb3d6..52ee2a0b 100644 --- a/llvm/src/expression/rvalue/dispatch.rs +++ b/llvm/src/expression/rvalue/dispatch.rs @@ -36,52 +36,41 @@ pub(crate) fn gen_expr<'ctx, 'a>( } else { variables::gen(env, name, expected_type) } - } + }, Expression::Deref(inner) => pointers::gen_deref(env, inner), Expression::AddressOf(inner) => pointers::gen_addressof(env, inner, expected_type), - Expression::MethodCall { - object, name, args, .. - } => calls::gen_method_call(env, object, name, args), - Expression::FunctionCall { - name, - type_args, - args, - } => { + Expression::MethodCall { object, name, args, .. } => { + calls::gen_method_call(env, object, name, args) + }, + Expression::FunctionCall { name, type_args, args } => { if env.program.variant_construction_of(expr).is_some() { variants::gen_constructor(env, expr, args) } else { calls::gen_function_call(env, name, type_args, args, expected_type) } - } + }, Expression::Cast { .. } => panic!("ICE: cast missing verified HIR conversion sequence"), - Expression::AssignOperation { - target, - operator, - value, - } => assign::gen_assign_operation(env, target, operator, value), + Expression::AssignOperation { target, operator, value } => { + assign::gen_assign_operation(env, target, operator, value) + }, Expression::Assignment { target, value } => assign::gen_assignment(env, target, value), - Expression::BinaryExpression { - left, - operator, - right, - } => binary::gen(env, left, operator, right, expected_type), + Expression::BinaryExpression { left, operator, right } => { + binary::gen(env, left, operator, right, expected_type) + }, Expression::IndexAccess { target, index } => index::gen(env, expr, target, index), - Expression::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => asm::gen(env, instructions, inputs, outputs, clobbers), + Expression::AsmBlock { instructions, inputs, outputs, clobbers } => { + asm::gen(env, instructions, inputs, outputs, clobbers) + }, Expression::StructLiteral { name, fields } => { structs::gen_struct_literal(env, name, fields) - } + }, Expression::FieldAccess { object, field } => structs::gen_field_access(env, object, field), Expression::Unary { operator, expr } => unary::gen(env, operator, expr, expected_type), @@ -89,10 +78,10 @@ pub(crate) fn gen_expr<'ctx, 'a>( Expression::Await(_) => { unreachable!("async lowering must remove await before LLVM emission") - } + }, Expression::Grouped(inner) => env.gen(inner, expected_type), Expression::ArrayLiteral(elements) => { arrays::gen_array_literal(env, elements, expected_type) - } + }, } } diff --git a/llvm/src/expression/rvalue/incdec.rs b/llvm/src/expression/rvalue/incdec.rs index 9764baab..ecd078ef 100644 --- a/llvm/src/expression/rvalue/incdec.rs +++ b/llvm/src/expression/rvalue/incdec.rs @@ -24,9 +24,7 @@ use inkwell::values::{BasicValue, BasicValueEnum}; use parser::ast::{Expression, IncDecKind, WaveType}; fn normalize_struct_name(raw: &str) -> &str { - raw.strip_prefix("struct.") - .unwrap_or(raw) - .trim_start_matches('%') + raw.strip_prefix("struct.").unwrap_or(raw).trim_start_matches('%') } fn resolve_struct_key<'ctx, 'a>( @@ -60,7 +58,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> Expression::Grouped(inner) => wave_type_of_lvalue(env, inner), Expression::AddressOf(inner) => { wave_type_of_lvalue(env, inner).map(|t| WaveType::Pointer(Box::new(t))) - } + }, Expression::Deref(inner) => { let inner_ty = wave_type_of_lvalue(env, inner)?; match inner_ty { @@ -68,7 +66,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> WaveType::String => Some(WaveType::Byte), _ => None, } - } + }, Expression::IndexAccess { target, .. } => { let t = wave_type_of_lvalue(env, target)?; match t { @@ -77,7 +75,7 @@ fn wave_type_of_lvalue<'ctx, 'a>(env: &ExprGenEnv<'ctx, 'a>, e: &Expression) -> WaveType::String => Some(WaveType::Byte), _ => None, } - } + }, _ => None, } } @@ -89,13 +87,13 @@ fn infer_lvalue_value_type<'ctx, 'a>( match target { Expression::Grouped(inner) | Expression::AddressOf(inner) => { infer_lvalue_value_type(env, inner) - } + }, Expression::Variable(_) | Expression::Deref(_) | Expression::IndexAccess { .. } => { let wt = wave_type_of_lvalue(env, target) .unwrap_or_else(|| panic!("Cannot infer lvalue type: {:?}", target)); wave_to_basic(env, &wt) - } + }, Expression::FieldAccess { object, field } => { let struct_name_opt = wave_type_of_lvalue(env, object).and_then(|wt| match wt { @@ -121,10 +119,7 @@ fn infer_lvalue_value_type<'ctx, 'a>( .unwrap_or_else(|| panic!("Unknown field '{}.{}'", struct_name, field)); return st.get_field_type_at_index(field_index).unwrap_or_else(|| { - panic!( - "Invalid field index {} for struct {}", - field_index, struct_name - ) + panic!("Invalid field index {} for struct {}", field_index, struct_name) }); } @@ -147,12 +142,9 @@ fn infer_lvalue_value_type<'ctx, 'a>( .unwrap_or_else(|| panic!("Unknown field '{}.{}'", struct_key, field)); st.get_field_type_at_index(field_index).unwrap_or_else(|| { - panic!( - "Invalid field index {} for struct {}", - field_index, struct_key - ) + panic!("Invalid field index {} for struct {}", field_index, struct_key) }) - } + }, _ => panic!("Expression is not an assignable lvalue: {:?}", target), } @@ -180,11 +172,8 @@ pub(crate) fn gen<'ctx, 'a>( let ptr = generate_address_ir(env, target); let element_type = infer_lvalue_value_type(env, target); - let old_val = env - .builder - .build_load(element_type, ptr, "incdec_old") - .unwrap() - .as_basic_value_enum(); + let old_val = + env.builder.build_load(element_type, ptr, "incdec_old").unwrap().as_basic_value_enum(); let new_val: BasicValueEnum<'ctx> = match old_val { BasicValueEnum::IntValue(iv) => { @@ -196,35 +185,35 @@ pub(crate) fn gen<'ctx, 'a>( let nv = match kind { IncDecKind::PreInc | IncDecKind::PostInc => { env.builder.build_int_add(iv, one, "inc").unwrap() - } + }, IncDecKind::PreDec | IncDecKind::PostDec => { env.builder.build_int_sub(iv, one, "dec").unwrap() - } + }, }; nv.as_basic_value_enum() - } + }, BasicValueEnum::FloatValue(fv) => { let one = fv.get_type().const_float(1.0); let nv = match kind { IncDecKind::PreInc | IncDecKind::PostInc => { env.builder.build_float_add(fv, one, "finc").unwrap() - } + }, IncDecKind::PreDec | IncDecKind::PostDec => { env.builder.build_float_sub(fv, one, "fdec").unwrap() - } + }, }; nv.as_basic_value_enum() - } + }, BasicValueEnum::PointerValue(pv) => { let idx = match kind { IncDecKind::PreInc | IncDecKind::PostInc => { env.context.i64_type().const_int(1, true) - } + }, IncDecKind::PreDec | IncDecKind::PostDec => { env.context.i64_type().const_int((-1i64) as u64, true) - } + }, }; let pointee_ty = infer_ptr_pointee_type(env, target); @@ -232,12 +221,10 @@ pub(crate) fn gen<'ctx, 'a>( // source pointer and stepped result to satisfy LLVM's inbounds GEP // contract for the same allocation. let gep = unsafe { - env.builder - .build_in_bounds_gep(pointee_ty, pv, &[idx], "pincdec") - .unwrap() + env.builder.build_in_bounds_gep(pointee_ty, pv, &[idx], "pincdec").unwrap() }; gep.as_basic_value_enum() - } + }, _ => panic!("Unsupported type for ++/--: {:?}", old_val), }; diff --git a/llvm/src/expression/rvalue/index.rs b/llvm/src/expression/rvalue/index.rs index fa1402a6..2cb26f4e 100644 --- a/llvm/src/expression/rvalue/index.rs +++ b/llvm/src/expression/rvalue/index.rs @@ -36,8 +36,5 @@ pub(crate) fn gen<'ctx, 'a>( return addr.as_basic_value_enum(); } - env.builder - .build_load(elem_ty, addr, "load_index_elem") - .unwrap() - .as_basic_value_enum() + env.builder.build_load(elem_ty, addr, "load_index_elem").unwrap().as_basic_value_enum() } diff --git a/llvm/src/expression/rvalue/literals.rs b/llvm/src/expression/rvalue/literals.rs index ba9589d7..967785c5 100644 --- a/llvm/src/expression/rvalue/literals.rs +++ b/llvm/src/expression/rvalue/literals.rs @@ -32,15 +32,10 @@ pub(crate) fn gen_null<'ctx, 'a>( ) -> BasicValueEnum<'ctx> { match expected_type { Some(BasicTypeEnum::PointerType(ptr_ty)) => ptr_ty.const_null().as_basic_value_enum(), - Some(other) => panic!( - "null literal can only be used with pointer expected type, got {:?}", - other - ), - None => env - .context - .ptr_type(AddressSpace::default()) - .const_null() - .as_basic_value_enum(), + Some(other) => { + panic!("null literal can only be used with pointer expected type, got {:?}", other) + }, + None => env.context.ptr_type(AddressSpace::default()).const_null().as_basic_value_enum(), } } @@ -65,12 +60,12 @@ pub(crate) fn gen<'ctx, 'a>( } iv.as_basic_value_enum() - } + }, Some(BasicTypeEnum::ArrayType(at)) => { let elem = at.get_element_type(); return gen(env, lit, Some(elem)); - } + }, Some(BasicTypeEnum::PointerType(ptr_ty)) => { if is_zero_int_literal(v) { @@ -80,14 +75,14 @@ pub(crate) fn gen<'ctx, 'a>( "integer literals cannot initialize pointers; use `null` or an explicit cast", ) } - } + }, Some(BasicTypeEnum::FloatType(ft)) => { let bits = if ft == env.context.f32_type() { 32 } else { 64 }; let f = hir::integer_literal_float(v, bits) .unwrap_or_else(|| panic!("invalid float literal from int token: {}", v)); ft.const_float(f).as_basic_value_enum() - } + }, None => { // Default untyped integer literal to i32 when no contextual type exists. @@ -105,18 +100,15 @@ pub(crate) fn gen<'ctx, 'a>( } iv.as_basic_value_enum() - } + }, - _ => panic!( - "Unsupported expected_type for int literal: {:?}", - expected_type - ), + _ => panic!("Unsupported expected_type for int literal: {:?}", expected_type), }, Literal::Float(value) => match expected_type { Some(BasicTypeEnum::FloatType(float_ty)) => { float_ty.const_float(*value).as_basic_value_enum() - } + }, Some(BasicTypeEnum::IntType(int_ty)) => env .builder .build_float_to_signed_int( @@ -126,11 +118,7 @@ pub(crate) fn gen<'ctx, 'a>( ) .unwrap() .as_basic_value_enum(), - None => env - .context - .f32_type() - .const_float(*value) - .as_basic_value_enum(), + None => env.context.f32_type().const_float(*value).as_basic_value_enum(), _ => panic!("Unsupported expected_type for float"), }, @@ -141,10 +129,7 @@ pub(crate) fn gen<'ctx, 'a>( let global_name = "str_lit"; - let str_type = env - .context - .i8_type() - .array_type(null_terminated.len() as u32); + let str_type = env.context.i8_type().array_type(null_terminated.len() as u32); let global = env.module.add_global(str_type, None, global_name); global.set_initializer(&env.context.const_string(&null_terminated, false)); @@ -160,23 +145,13 @@ pub(crate) fn gen<'ctx, 'a>( }; gep.as_basic_value_enum() - } - - Literal::Bool(v) => env - .context - .bool_type() - .const_int(if *v { 1 } else { 0 }, false) - .as_basic_value_enum(), - - Literal::Char(c) => env - .context - .i8_type() - .const_int(*c as u64, false) - .as_basic_value_enum(), - Literal::Byte(b) => env - .context - .i8_type() - .const_int(*b as u64, false) - .as_basic_value_enum(), + }, + + Literal::Bool(v) => { + env.context.bool_type().const_int(if *v { 1 } else { 0 }, false).as_basic_value_enum() + }, + + Literal::Char(c) => env.context.i8_type().const_int(*c as u64, false).as_basic_value_enum(), + Literal::Byte(b) => env.context.i8_type().const_int(*b as u64, false).as_basic_value_enum(), } } diff --git a/llvm/src/expression/rvalue/mod.rs b/llvm/src/expression/rvalue/mod.rs index 86b43e73..eac0a67d 100644 --- a/llvm/src/expression/rvalue/mod.rs +++ b/llvm/src/expression/rvalue/mod.rs @@ -84,16 +84,13 @@ impl<'ctx, 'a> ExprGenEnv<'ctx, 'a> { let mut value = match expr { Expression::Cast { expr: inner, .. } | Expression::Grouped(inner) => { self.gen(inner, None) - } - Expression::BinaryExpression { - left, - operator, - right, - } if fact.computation_type.is_some() - && !matches!( - operator, - parser::ast::Operator::LogicalAnd | parser::ast::Operator::LogicalOr - ) => + }, + Expression::BinaryExpression { left, operator, right } + if fact.computation_type.is_some() + && !matches!( + operator, + parser::ast::Operator::LogicalAnd | parser::ast::Operator::LogicalOr + ) => { let left = self.gen(left, None); let right = self.gen(right, None); @@ -107,7 +104,7 @@ impl<'ctx, 'a> ExprGenEnv<'ctx, 'a> { fact.computation_type.as_ref().unwrap(), fact.shift_count_type.as_ref(), ) - } + }, _ => dispatch::gen_expr(self, expr, Some(native)), }; // Bool's memory/C-ABI representation is i8; its semantic value is i1. diff --git a/llvm/src/expression/rvalue/pointers.rs b/llvm/src/expression/rvalue/pointers.rs index 68765d95..5c5f7d1f 100644 --- a/llvm/src/expression/rvalue/pointers.rs +++ b/llvm/src/expression/rvalue/pointers.rs @@ -36,24 +36,19 @@ pub(crate) fn gen_deref<'ctx, 'a>( Expression::IndexAccess { .. } | Expression::FieldAccess { .. } => { let (addr, load_ty) = generate_address_and_type_ir(env, inner_expr); return env.builder.build_load(load_ty, addr, "deref_load").unwrap(); - } - _ => {} + }, + _ => {}, } let pointee = match env.wave_type(inner_expr) { Some(WaveType::Pointer(inner)) => *inner, Some(WaveType::String) => WaveType::Byte, - other => panic!( - "typed HIR did not provide a pointer type for deref: {:?}", - other - ), + other => panic!("typed HIR did not provide a pointer type for deref: {:?}", other), }; let load_ty = wave_type_to_llvm_type(env.context, &pointee, env.struct_types, TypeFlavor::Value); let pointer = env.gen(inner_expr, None).into_pointer_value(); - env.builder - .build_load(load_ty, pointer, "deref_load") - .unwrap() + env.builder.build_load(load_ty, pointer, "deref_load").unwrap() } pub(crate) fn gen_addressof<'ctx, 'a>( @@ -72,10 +67,7 @@ pub(crate) fn gen_addressof<'ctx, 'a>( let array_type = wave_type_to_llvm_type(env.context, array_type, env.struct_types, TypeFlavor::Value); let value = env.gen(inner_expr, Some(array_type)); - let storage = env - .builder - .build_alloca(array_type, "addressed_array") - .unwrap(); + let storage = env.builder.build_alloca(array_type, "addressed_array").unwrap(); env.builder.build_store(storage, value).unwrap(); return storage.into(); } diff --git a/llvm/src/expression/rvalue/structs.rs b/llvm/src/expression/rvalue/structs.rs index 2a281a66..21398865 100644 --- a/llvm/src/expression/rvalue/structs.rs +++ b/llvm/src/expression/rvalue/structs.rs @@ -27,20 +27,15 @@ pub(crate) fn gen_struct_literal<'ctx, 'a>( name: &str, fields: &[(String, Expression)], ) -> BasicValueEnum<'ctx> { - let struct_ty = *env - .struct_types - .get(name) - .unwrap_or_else(|| panic!("Struct type '{}' not found", name)); + let struct_ty = + *env.struct_types.get(name).unwrap_or_else(|| panic!("Struct type '{}' not found", name)); let field_indices = env .struct_field_indices .get(name) .unwrap_or_else(|| panic!("Field index map for struct '{}' not found", name)); - let tmp_alloca = env - .builder - .build_alloca(struct_ty, &format!("tmp_{}_literal", name)) - .unwrap(); + let tmp_alloca = env.builder.build_alloca(struct_ty, &format!("tmp_{}_literal", name)).unwrap(); for (field_name, field_expr) in fields { let idx = *field_indices @@ -64,23 +59,14 @@ pub(crate) fn gen_struct_literal<'ctx, 'a>( let field_ptr = env .builder - .build_struct_gep( - struct_ty, - tmp_alloca, - idx, - &format!("{}.{}", name, field_name), - ) + .build_struct_gep(struct_ty, tmp_alloca, idx, &format!("{}.{}", name, field_name)) .unwrap(); env.builder.build_store(field_ptr, field_val).unwrap(); } env.builder - .build_load( - struct_ty.as_basic_type_enum(), - tmp_alloca, - &format!("{}_literal_val", name), - ) + .build_load(struct_ty.as_basic_type_enum(), tmp_alloca, &format!("{}_literal_val", name)) .unwrap() .as_basic_value_enum() } @@ -97,9 +83,7 @@ pub(crate) fn gen_field_access<'ctx, 'a>( // Keep the original receiver identity so typed HIR remains authoritative. // A returned struct is a value; reconstructing an lvalue would require an // address that function/method call results do not have. - let receiver_type = env - .wave_type(object) - .expect("field receiver has a resolved HIR type"); + let receiver_type = env.wave_type(object).expect("field receiver has a resolved HIR type"); let struct_name = match &receiver_type { WaveType::Struct(name) => name, WaveType::Pointer(inner) => match inner.as_ref() { @@ -119,10 +103,8 @@ pub(crate) fn gen_field_access<'ctx, 'a>( .build_extract_value(value, index, &format!("field_{field}")) .expect("field index matches the resolved struct"), BasicValueEnum::PointerValue(pointer) => { - let struct_type = *env - .struct_types - .get(struct_name) - .expect("resolved struct has an LLVM type"); + let struct_type = + *env.struct_types.get(struct_name).expect("resolved struct has an LLVM type"); let field_type = struct_type .get_field_type_at_index(index) .expect("resolved field has an LLVM type"); @@ -133,7 +115,7 @@ pub(crate) fn gen_field_access<'ctx, 'a>( env.builder .build_load(field_type, pointer, &format!("load_field_{field}")) .expect("field pointer has the resolved field type") - } + }, other => panic!("resolved struct receiver lowered to an invalid LLVM value: {other:?}"), } } diff --git a/llvm/src/expression/rvalue/unary.rs b/llvm/src/expression/rvalue/unary.rs index 82a5f311..bbc2389b 100644 --- a/llvm/src/expression/rvalue/unary.rs +++ b/llvm/src/expression/rvalue/unary.rs @@ -33,27 +33,19 @@ pub(crate) fn gen<'ctx, 'a>( // - (negation) (Operator::Neg, BasicValueEnum::IntValue(iv)) => { let zero = iv.get_type().const_zero(); - env.builder - .build_int_sub(zero, iv, "neg") - .unwrap() - .as_basic_value_enum() - } + env.builder.build_int_sub(zero, iv, "neg").unwrap().as_basic_value_enum() + }, - (Operator::Neg, BasicValueEnum::FloatValue(fv)) => env - .builder - .build_float_neg(fv, "fneg") - .unwrap() - .as_basic_value_enum(), + (Operator::Neg, BasicValueEnum::FloatValue(fv)) => { + env.builder.build_float_neg(fv, "fneg").unwrap().as_basic_value_enum() + }, // ! (logical not) (Operator::LogicalNot, BasicValueEnum::IntValue(iv)) | (Operator::Not, BasicValueEnum::IntValue(iv)) => { let bw = iv.get_type().get_bit_width(); if bw == 1 { - env.builder - .build_not(iv, "lnot") - .unwrap() - .as_basic_value_enum() + env.builder.build_not(iv, "lnot").unwrap().as_basic_value_enum() } else { let zero = iv.get_type().const_zero(); env.builder @@ -61,18 +53,13 @@ pub(crate) fn gen<'ctx, 'a>( .unwrap() .as_basic_value_enum() } - } + }, // ~ (bitwise not) - (Operator::BitwiseNot, BasicValueEnum::IntValue(iv)) => env - .builder - .build_not(iv, "bnot") - .unwrap() - .as_basic_value_enum(), + (Operator::BitwiseNot, BasicValueEnum::IntValue(iv)) => { + env.builder.build_not(iv, "bnot").unwrap().as_basic_value_enum() + }, - _ => panic!( - "Unsupported unary operator {:?} for value {:?}", - operator, val - ), + _ => panic!("Unsupported unary operator {:?} for value {:?}", operator, val), } } diff --git a/llvm/src/expression/rvalue/utils.rs b/llvm/src/expression/rvalue/utils.rs index 02987e3f..2a866834 100644 --- a/llvm/src/expression/rvalue/utils.rs +++ b/llvm/src/expression/rvalue/utils.rs @@ -22,7 +22,5 @@ pub(crate) fn to_bool<'ctx>(builder: &Builder<'ctx>, v: IntValue<'ctx>) -> IntVa } let zero = v.get_type().const_zero(); - builder - .build_int_compare(IntPredicate::NE, v, zero, "tobool") - .unwrap() + builder.build_int_compare(IntPredicate::NE, v, zero, "tobool").unwrap() } diff --git a/llvm/src/expression/rvalue/variables.rs b/llvm/src/expression/rvalue/variables.rs index c8305c3e..21d93426 100644 --- a/llvm/src/expression/rvalue/variables.rs +++ b/llvm/src/expression/rvalue/variables.rs @@ -29,17 +29,9 @@ pub(crate) fn gen<'ctx, 'a>( expected_type: Option>, ) -> BasicValueEnum<'ctx> { if var_name == "true" { - return env - .context - .bool_type() - .const_int(1, false) - .as_basic_value_enum(); + return env.context.bool_type().const_int(1, false).as_basic_value_enum(); } else if var_name == "false" { - return env - .context - .bool_type() - .const_int(0, false) - .as_basic_value_enum(); + return env.context.bool_type().const_int(0, false).as_basic_value_enum(); } if let Some(var_info) = env.variables.get(var_name) { @@ -47,10 +39,8 @@ pub(crate) fn gen<'ctx, 'a>( if let Some(et) = expected_type { if et.is_pointer_type() { - let loaded = env - .builder - .build_load(et, ptr, &format!("load_{}", var_name)) - .unwrap(); + let loaded = + env.builder.build_load(et, ptr, &format!("load_{}", var_name)).unwrap(); let expected_ptr = et.into_pointer_type(); if loaded.get_type() != BasicTypeEnum::from(expected_ptr) { @@ -80,7 +70,7 @@ pub(crate) fn gen<'ctx, 'a>( .build_load(load_ty, ptr, &format!("load_{}", var_name)) .unwrap() .as_basic_value_enum() - } + }, } } else if let Some(const_val) = env.global_consts.get(var_name) { *const_val diff --git a/llvm/src/expression/rvalue/variants.rs b/llvm/src/expression/rvalue/variants.rs index 0c1bd3ae..f7efe299 100644 --- a/llvm/src/expression/rvalue/variants.rs +++ b/llvm/src/expression/rvalue/variants.rs @@ -32,28 +32,18 @@ pub(crate) fn gen_constructor<'ctx, 'a>( let WaveType::Variant(name) = &construction.variant_type else { panic!("variant constructor metadata has a non-variant type"); }; - let variant_ty = *env - .struct_types - .get(name) - .unwrap_or_else(|| panic!("variant type '{}' not found", name)); - let value_ptr = env - .builder - .build_alloca(variant_ty, &format!("variant.{}.value", name)) - .unwrap(); - env.builder - .build_store(value_ptr, variant_ty.const_zero()) - .unwrap(); + let variant_ty = + *env.struct_types.get(name).unwrap_or_else(|| panic!("variant type '{}' not found", name)); + let value_ptr = + env.builder.build_alloca(variant_ty, &format!("variant.{}.value", name)).unwrap(); + env.builder.build_store(value_ptr, variant_ty.const_zero()).unwrap(); - let tag_ptr = env - .builder - .build_struct_gep(variant_ty, value_ptr, 0, "variant.tag.ptr") - .unwrap(); + let tag_ptr = + env.builder.build_struct_gep(variant_ty, value_ptr, 0, "variant.tag.ptr").unwrap(); env.builder .build_store( tag_ptr, - env.context - .i32_type() - .const_int(construction.discriminant as u64, false), + env.context.i32_type().const_int(construction.discriminant as u64, false), ) .unwrap(); @@ -68,13 +58,8 @@ pub(crate) fn gen_constructor<'ctx, 'a>( name, construction.case_name ); } - let payload_ptr = env - .builder - .build_alloca(payload_ty, "variant.payload.value") - .unwrap(); - env.builder - .build_store(payload_ptr, payload_ty.const_zero()) - .unwrap(); + let payload_ptr = env.builder.build_alloca(payload_ty, "variant.payload.value").unwrap(); + env.builder.build_store(payload_ptr, payload_ty.const_zero()).unwrap(); for (index, (argument, payload_wave_type)) in args.iter().zip(&construction.payload_types).enumerate() @@ -104,16 +89,10 @@ pub(crate) fn gen_constructor<'ctx, 'a>( let payload = env .builder - .build_load( - payload_ty.as_basic_type_enum(), - payload_ptr, - "variant.payload", - ) - .unwrap(); - let case_ptr = env - .builder - .build_struct_gep(variant_ty, value_ptr, 2, "variant.case.ptr") + .build_load(payload_ty.as_basic_type_enum(), payload_ptr, "variant.payload") .unwrap(); + let case_ptr = + env.builder.build_struct_gep(variant_ty, value_ptr, 2, "variant.case.ptr").unwrap(); env.builder.build_store(case_ptr, payload).unwrap(); env.builder .build_load(variant_ty.as_basic_type_enum(), value_ptr, "variant.value") diff --git a/llvm/src/importgen.rs b/llvm/src/importgen.rs index e46f35ec..d1bda51b 100644 --- a/llvm/src/importgen.rs +++ b/llvm/src/importgen.rs @@ -39,7 +39,7 @@ fn expand_imports_recursive( let expanded = expand_imports_recursive(imported.ast, next_dir, already)?; out.extend(expanded); - } + }, other => out.push(other), } } diff --git a/llvm/src/msvc/coff.rs b/llvm/src/msvc/coff.rs index ee863a64..86e42bc3 100644 --- a/llvm/src/msvc/coff.rs +++ b/llvm/src/msvc/coff.rs @@ -9,39 +9,34 @@ fn range(data: &[u8], offset: usize, size: usize) -> Result<&[u8], String> { .and_then(|end| data.get(offset..end)) .ok_or_else(|| format!("truncated COFF input at offset {offset}, length {size}")) } + fn u16_at(data: &[u8], offset: usize) -> Result { - Ok(u16::from_le_bytes( - range(data, offset, 2)?.try_into().unwrap(), - )) + Ok(u16::from_le_bytes(range(data, offset, 2)?.try_into().unwrap())) } + fn u32_at(data: &[u8], offset: usize) -> Result { Ok(u32::from_le_bytes(range(data, offset, 4)?.try_into().unwrap()) as usize) } + fn table(data: &[u8], start: usize, count: usize, width: usize) -> Result<(), String> { - range( - data, - start, - count.checked_mul(width).ok_or("COFF table size overflow")?, - )?; + range(data, start, count.checked_mul(width).ok_or("COFF table size overflow")?)?; Ok(()) } + fn machine(actual: u16, expected: u16) -> Result<(), String> { if actual == expected { Ok(()) } else { - Err(format!( - "COFF machine 0x{actual:04x} does not match target machine 0x{expected:04x}" - )) + Err(format!("COFF machine 0x{actual:04x} does not match target machine 0x{expected:04x}")) } } + fn object(data: &[u8], expected: u16) -> Result<(), String> { if data.starts_with(b"BC\xc0\xde") || data.starts_with(&[0xde, 0xc0, 0x17, 0x0b]) { return Err("LLVM bitcode inputs are not supported by the MSVC input inspector; emit a target COFF object first".into()); } if data.starts_with(b"MZ") { - return Err( - "PE images are not relocatable objects; link the import library instead".into(), - ); + return Err("PE images are not relocatable objects; link the import library instead".into()); } range(data, 0, 20)?; // VC SDK libraries include machine-neutral debug/weak-alias objects. They @@ -70,29 +65,17 @@ fn object(data: &[u8], expected: u16) -> Result<(), String> { if version < 2 || range(data, 12, 16)? != BIGOBJ { return Err("unsupported anonymous COFF object (expected bigobj)".into()); } - ( - 56, - u32_at(data, 44)?, - u32_at(data, 48)?, - u32_at(data, 52)?, - 20, - ) + (56, u32_at(data, 44)?, u32_at(data, 48)?, u32_at(data, 52)?, 20) } else { if !neutral { machine(u16_at(data, 0)?, expected)?; } if u16_at(data, 16)? != 0 || u16_at(data, 18)? & 2 != 0 { return Err( - "expected a relocatable COFF object without an optional image header".into(), + "expected a relocatable COFF object without an optional image header".into() ); } - ( - 20, - u16_at(data, 2)? as usize, - u32_at(data, 8)?, - u32_at(data, 12)?, - 18, - ) + (20, u16_at(data, 2)? as usize, u32_at(data, 8)?, u32_at(data, 12)?, 18) }; table(data, header, sections, 40)?; for index in 0..sections { @@ -140,11 +123,7 @@ fn object(data: &[u8], expected: u16) -> Result<(), String> { if symbols != 0 { table(data, symbols, count, symbol_width)?; let strings = symbols - .checked_add( - count - .checked_mul(symbol_width) - .ok_or("symbol size overflow")?, - ) + .checked_add(count.checked_mul(symbol_width).ok_or("symbol size overflow")?) .ok_or("symbol offset overflow")?; let size = u32_at(data, strings)?; if size < 4 { @@ -189,9 +168,7 @@ fn inspect_with_policy(data: &[u8], target: &str, lazy_archive: bool) -> Result< .trim() .parse() .map_err(|_| "invalid archive size")?; - let name = std::str::from_utf8(&header[..16]) - .map_err(|_| "invalid archive name")? - .trim(); + let name = std::str::from_utf8(&header[..16]).map_err(|_| "invalid archive name")?.trim(); let mut payload = range(data, offset + 60, length)?; let mut display = name.to_string(); if name == "/" { @@ -205,27 +182,19 @@ fn inspect_with_policy(data: &[u8], target: &str, lazy_archive: bool) -> Result< } else if name != "/SYM64/" { member_offsets.insert(offset); if let Some(size) = name.strip_prefix("#1/") { - let size: usize = size - .parse() - .map_err(|_| "invalid BSD archive name length")?; + let size: usize = size.parse().map_err(|_| "invalid BSD archive name length")?; display = String::from_utf8_lossy(range(payload, 0, size)?) .trim_end_matches('\0') .to_string(); payload = &payload[size..]; } else if let Some(index) = name.strip_prefix('/') { - let index: usize = index - .parse() - .map_err(|_| "invalid archive long-name offset")?; - let tail = names - .get(index..) - .ok_or("archive long-name offset out of bounds")?; + let index: usize = index.parse().map_err(|_| "invalid archive long-name offset")?; + let tail = names.get(index..).ok_or("archive long-name offset out of bounds")?; let end = tail .iter() .position(|b| *b == 0 || *b == b'\n') .ok_or("unterminated archive long name")?; - display = String::from_utf8_lossy(&tail[..end]) - .trim_end_matches('/') - .to_string(); + display = String::from_utf8_lossy(&tail[..end]).trim_end_matches('/').to_string(); } objects.push((offset, display, payload)); } @@ -283,12 +252,7 @@ fn inspect_with_policy(data: &[u8], target: &str, lazy_archive: bool) -> Result< } native_members.insert(second_offsets[member - 1]); } - if payload[end + 4 + symbols * 2..] - .iter() - .filter(|b| **b == 0) - .count() - < symbols - { + if payload[end + 4 + symbols * 2..].iter().filter(|b| **b == 0).count() < symbols { return Err("truncated archive symbol names".into()); } } @@ -316,14 +280,7 @@ fn inspect_with_policy(data: &[u8], target: &str, lazy_archive: bool) -> Result< // //. SDK hybrid libraries may carry EC-only members. Still // validate their structure, and never exempt directly supplied objects, // foreign members in ordinary archives, or /WHOLEARCHIVE members. - let actual = u16_at( - payload, - if payload.starts_with(&[0, 0, 255, 255]) { - 6 - } else { - 0 - }, - )?; + let actual = u16_at(payload, if payload.starts_with(&[0, 0, 255, 255]) { 6 } else { 0 })?; let ec_only = lazy_archive && expected == 0xaa64 && matches!(actual, 0xa641 | 0x8664) diff --git a/llvm/src/msvc/response.rs b/llvm/src/msvc/response.rs index bdad4bd9..d215b81f 100644 --- a/llvm/src/msvc/response.rs +++ b/llvm/src/msvc/response.rs @@ -19,10 +19,7 @@ fn quote(argument: &str) -> String { slashes += 1; continue; } - quoted.extend(std::iter::repeat_n( - '\\', - if ch == '"' { slashes * 2 + 1 } else { slashes }, - )); + quoted.extend(std::iter::repeat_n('\\', if ch == '"' { slashes * 2 + 1 } else { slashes })); quoted.push(ch); slashes = 0; } @@ -42,15 +39,9 @@ impl LinkArguments { let lines: Vec<_> = arguments.iter().map(|arg| quote(arg)).collect(); let length = program.encode_utf16().count() + 3 - + lines - .iter() - .map(|arg| arg.encode_utf16().count() + 1) - .sum::(); + + lines.iter().map(|arg| arg.encode_utf16().count() + 1).sum::(); if length < COMMAND_BUDGET { - return Ok(Self { - arguments: arguments.to_vec(), - _response: None, - }); + return Ok(Self { arguments: arguments.to_vec(), _response: None }); } let response = PendingOutput::new(output)?; // Both MSVC LINK and LLVM's response-file reader accept UTF-16LE BOM. @@ -90,14 +81,8 @@ mod tests { unsafe extern "system" { fn LocalFree(memory: *mut std::ffi::c_void) -> *mut std::ffi::c_void; } - let original = [ - "link.exe", - "", - "한글 object.obj", - "a\\\"b", - "C:\\space here\\", - "literal\"quote", - ]; + let original = + ["link.exe", "", "한글 object.obj", "a\\\"b", "C:\\space here\\", "literal\"quote"]; let command: Vec = original .iter() .map(|arg| quote(arg)) @@ -119,9 +104,7 @@ mod tests { while *value.add(length) != 0 { length += 1; } - values.push(String::from_utf16_lossy(std::slice::from_raw_parts( - value, length, - ))); + values.push(String::from_utf16_lossy(std::slice::from_raw_parts(value, length))); } LocalFree(argv.cast()); values @@ -144,13 +127,7 @@ mod tests { .collect::>(), ) .unwrap(); - assert_eq!( - decoded, - args.iter() - .map(|arg| quote(arg)) - .collect::>() - .join("\n") - ); + assert_eq!(decoded, args.iter().map(|arg| quote(arg)).collect::>().join("\n")); drop(response); assert!(!path.exists()); let short = LinkArguments::prepare("lld-link", &["/NOLOGO".into()], &output).unwrap(); diff --git a/llvm/src/msvc/runtime.rs b/llvm/src/msvc/runtime.rs index 5be0f323..800b5f8a 100644 --- a/llvm/src/msvc/runtime.rs +++ b/llvm/src/msvc/runtime.rs @@ -11,17 +11,13 @@ pub fn find_builtins(target: &str, roots: &[PathBuf]) -> Option { roots .iter() .map(|root| { - root.join("lib/clang/21/lib/windows") - .join(format!("clang_rt.builtins-{arch}.lib")) + root.join("lib/clang/21/lib/windows").join(format!("clang_rt.builtins-{arch}.lib")) }) .find(|path| path.is_file()) } pub fn add_builtins(target: &str, args: &mut Vec) { - if args - .iter() - .any(|arg| arg.eq_ignore_ascii_case("/NODEFAULTLIB")) - { + if args.iter().any(|arg| arg.eq_ignore_ascii_case("/NODEFAULTLIB")) { return; } let mut roots = Vec::new(); diff --git a/llvm/src/msvc/sdk.rs b/llvm/src/msvc/sdk.rs index 068cd895..cce5ab78 100644 --- a/llvm/src/msvc/sdk.rs +++ b/llvm/src/msvc/sdk.rs @@ -12,6 +12,7 @@ fn get<'a>(env: &'a Environment, key: &str) -> Option<&'a str> { .map(|(_, v)| v.as_str()) .filter(|v| !v.is_empty()) } + fn versions(root: &Path) -> Vec { let mut entries: Vec<_> = std::fs::read_dir(root) .into_iter() @@ -33,11 +34,13 @@ fn versions(root: &Path) -> Vec { entries.sort_by(|a, b| b.0.cmp(&a.0)); entries.into_iter().map(|(_, p)| p).collect() } + fn add(paths: &mut Vec, path: PathBuf) { if path.is_dir() && !paths.contains(&path) { paths.push(path); } } + fn add_sdk(paths: &mut Vec, root: &Path, version: Option<&str>, arch: &str) { let lib = root.join("Lib"); let candidates = if let Some(v) = version { @@ -55,6 +58,7 @@ fn add_sdk(paths: &mut Vec, root: &Path, version: Option<&str>, arch: & } } } + fn add_vc(paths: &mut Vec, root: &Path, arch: &str) { let lib = root.join("lib").join(arch); if lib.join("vcruntime.lib").is_file() { @@ -63,11 +67,7 @@ fn add_vc(paths: &mut Vec, root: &Path, arch: &str) { } pub fn discover(target: &str, explicit: &[String], env: &Environment) -> Vec { - let arch = if target.starts_with("aarch64-") { - "arm64" - } else { - "x64" - }; + let arch = if target.starts_with("aarch64-") { "arm64" } else { "x64" }; let mut paths = explicit.iter().map(PathBuf::from).collect::>(); if let Some(lib) = get(env, "LIB") { for path in lib.split(';').filter(|p| !p.is_empty()) { @@ -75,19 +75,9 @@ pub fn discover(target: &str, explicit: &[String], env: &Environment) -> Vec Option { bytes: *mut u32, ) -> i32; } - let subkey: Vec = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0" - .encode_utf16() - .collect(); + let subkey: Vec = + "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0".encode_utf16().collect(); let value: Vec = "KitsRoot10\0".encode_utf16().collect(); let mut buffer = vec![0u16; 32768]; let mut bytes = (buffer.len() * 2) as u32; @@ -142,9 +131,7 @@ fn installed_sdk_root() -> Option { if status != 0 || bytes < 2 || bytes as usize > buffer.len() * 2 || bytes % 2 != 0 { return None; } - String::from_utf16(&buffer[..bytes as usize / 2 - 1]) - .ok() - .filter(|s| !s.is_empty()) + String::from_utf16(&buffer[..bytes as usize / 2 - 1]).ok().filter(|s| !s.is_empty()) } pub fn environment(target: &str) -> Environment { @@ -166,23 +153,11 @@ pub fn environment(target: &str) -> Environment { if let Some(programs) = get(&env, "ProgramFiles(x86)") { let exe = Path::new(programs).join("Microsoft Visual Studio/Installer/vswhere.exe"); if let Ok(out) = std::process::Command::new(exe) - .args([ - "-all", - "-sort", - "-utf8", - "-products", - "*", - "-property", - "installationPath", - ]) + .args(["-all", "-sort", "-utf8", "-products", "*", "-property", "installationPath"]) .output() { if out.status.success() { - let arch = if target.starts_with("aarch64-") { - "arm64" - } else { - "x64" - }; + let arch = if target.starts_with("aarch64-") { "arm64" } else { "x64" }; for root in String::from_utf8_lossy(&out.stdout).lines().map(str::trim) { if !root.is_empty() && versions(&Path::new(root).join("VC/Tools/MSVC")) @@ -206,11 +181,11 @@ pub fn discovered_arguments(target: &str, paths: &[String]) -> Vec { .map(|p| p.to_string_lossy().into_owned()) .collect() } + fn value<'a>(arg: &'a str, prefix: &str) -> Option<&'a str> { - arg.get(..prefix.len()) - .filter(|v| v.eq_ignore_ascii_case(prefix)) - .map(|_| &arg[prefix.len()..]) + arg.get(..prefix.len()).filter(|v| v.eq_ignore_ascii_case(prefix)).map(|_| &arg[prefix.len()..]) } + fn find_library(name: &str, paths: &[PathBuf]) -> Option { let name = name.trim_matches('"'); let file = if name.to_ascii_lowercase().ends_with(".lib") { @@ -228,11 +203,8 @@ fn find_library(name: &str, paths: &[PathBuf]) -> Option { /// Inspect explicitly resolved inputs before either linker is launched. Object /// directives are still interpreted by the linker; no claim of LTO validation. pub fn validate_arguments(target: &str, args: &[String]) -> Result<(), String> { - let paths: Vec<_> = args - .iter() - .filter_map(|a| value(a, "/LIBPATH:")) - .map(PathBuf::from) - .collect(); + let paths: Vec<_> = + args.iter().filter_map(|a| value(a, "/LIBPATH:")).map(PathBuf::from).collect(); let no_defaults = args.iter().any(|a| a.eq_ignore_ascii_case("/NODEFAULTLIB")); let mut files = Vec::new(); let mut required = Vec::new(); @@ -245,7 +217,7 @@ pub fn validate_arguments(target: &str, args: &[String]) -> Result<(), String> { required.push(lib); } else if arg.starts_with('@') { return Err( - "MSVC response-file input cannot be inspected; supply inputs directly".into(), + "MSVC response-file input cannot be inspected; supply inputs directly".into() ); } else if arg.starts_with('/') && arg.contains(':') && !Path::new(arg).is_file() { continue; @@ -258,7 +230,7 @@ pub fn validate_arguments(target: &str, args: &[String]) -> Result<(), String> { { Some("lib") => required.push(arg), Some("obj" | "o" | "a" | "bc") => files.push(PathBuf::from(arg)), - _ => {} + _ => {}, } } } diff --git a/llvm/src/statement/asm.rs b/llvm/src/statement/asm.rs index 7281f696..19db9117 100644 --- a/llvm/src/statement/asm.rs +++ b/llvm/src/statement/asm.rs @@ -32,14 +32,8 @@ use parser::ast::{Expression, Literal, WaveType}; use std::collections::HashMap; enum AsmOutPlace<'ctx> { - VarAlloca { - ptr: PointerValue<'ctx>, - elem_ty: BasicTypeEnum<'ctx>, - }, - MemPtr { - ptr: PointerValue<'ctx>, - elem_ty: BasicTypeEnum<'ctx>, - }, + VarAlloca { ptr: PointerValue<'ctx>, elem_ty: BasicTypeEnum<'ctx> }, + MemPtr { ptr: PointerValue<'ctx>, elem_ty: BasicTypeEnum<'ctx> }, } fn llvm_type_of_wave<'ctx>( @@ -175,9 +169,8 @@ pub(super) fn gen_asm_stmt_ir<'ctx>( false, ); - let call = builder - .build_indirect_call(fn_type, inline_asm, &operand_vals, "inline_asm") - .unwrap(); + let call = + builder.build_indirect_call(fn_type, inline_asm, &operand_vals, "inline_asm").unwrap(); if plan.noreturn { builder.build_unreachable().unwrap(); @@ -192,7 +185,7 @@ pub(super) fn gen_asm_stmt_ir<'ctx>( ValueKind::Basic(v) => v, ValueKind::Instruction(_) => { panic!("asm stmt expected return value but got instruction-only result"); - } + }, }; if out_places.len() == 1 { @@ -202,9 +195,7 @@ pub(super) fn gen_asm_stmt_ir<'ctx>( let struct_val = ret_val.into_struct_value(); for (idx, place) in out_places.iter().enumerate() { - let elem = builder - .build_extract_value(struct_val, idx as u32, "asm_out_elem") - .unwrap(); + let elem = builder.build_extract_value(struct_val, idx as u32, "asm_out_elem").unwrap(); store_asm_out_place(context, builder, place, elem, "asm_out"); } } @@ -219,8 +210,8 @@ fn infer_signedness<'ctx>( WaveType::Int(_) => return Some(true), WaveType::Uint(_) | WaveType::Byte | WaveType::Char | WaveType::Bool => { return Some(false) - } - _ => {} + }, + _ => {}, } } match expr { @@ -248,14 +239,14 @@ fn infer_signedness<'ctx>( } else { None } - } + }, Expression::Literal(Literal::Int(s)) => { if s.trim_start().starts_with('-') { Some(true) } else { None } - } + }, _ => None, } } @@ -269,18 +260,11 @@ fn resolve_out_place_and_type<'ctx>( ) -> (AsmOutPlace<'ctx>, BasicTypeEnum<'ctx>) { match target { Expression::Variable(name) => { - let info = variables - .get(name) - .unwrap_or_else(|| panic!("Output var '{}' not found", name)); + let info = + variables.get(name).unwrap_or_else(|| panic!("Output var '{}' not found", name)); let elem_ty = llvm_type_of_wave(context, &info.ty, struct_types); - ( - AsmOutPlace::VarAlloca { - ptr: info.ptr, - elem_ty, - }, - elem_ty, - ) - } + (AsmOutPlace::VarAlloca { ptr: info.ptr, elem_ty }, elem_ty) + }, Expression::Deref(inner) => { match inner.as_ref() { @@ -305,27 +289,18 @@ fn resolve_out_place_and_type<'ctx>( let elem_ty = match &info.ty { WaveType::Pointer(inner_ty) => { llvm_type_of_wave(context, inner_ty, struct_types) - } + }, WaveType::String => context.i8_type().as_basic_type_enum(), other => panic!("out(*{}) requires pointer/string, got {:?}", name, other), }; - ( - AsmOutPlace::MemPtr { - ptr: dst_ptr, - elem_ty, - }, - elem_ty, - ) - } + (AsmOutPlace::MemPtr { ptr: dst_ptr, elem_ty }, elem_ty) + }, other => panic!("Unsupported deref out target: {:?}", other), } - } + }, - other => panic!( - "out(...) target must be variable or deref var for now: {:?}", - other - ), + other => panic!("out(...) target must be variable or deref var for now: {:?}", other), } } @@ -340,11 +315,11 @@ fn store_asm_out_place<'ctx>( AsmOutPlace::VarAlloca { ptr, elem_ty } => { let v = coerce_basic_value_for_store(context, builder, value, *elem_ty, name); builder.build_store(*ptr, v).unwrap(); - } + }, AsmOutPlace::MemPtr { ptr, elem_ty } => { let v = coerce_basic_value_for_store(context, builder, value, *elem_ty, name); builder.build_store(*ptr, v).unwrap(); - } + }, } } @@ -463,8 +438,5 @@ fn coerce_basic_value_for_store<'ctx>( ); } - panic!( - "Unsupported destination type for asm output '{}': {:?}", - name, dst_ty - ); + panic!("Unsupported destination type for asm output '{}': {:?}", name, dst_ty); } diff --git a/llvm/src/statement/assign.rs b/llvm/src/statement/assign.rs index 8b0ccbc4..df38c206 100644 --- a/llvm/src/statement/assign.rs +++ b/llvm/src/statement/assign.rs @@ -50,9 +50,8 @@ pub(super) fn gen_assign_ir<'ctx>( } let (dst_ptr, dst_mutability, dst_wave_ty) = { - let info = variables - .get(variable) - .unwrap_or_else(|| panic!("Variable {} not declared", variable)); + let info = + variables.get(variable).unwrap_or_else(|| panic!("Variable {} not declared", variable)); (info.ptr, info.mutability.clone(), info.ty.clone()) }; diff --git a/llvm/src/statement/control.rs b/llvm/src/statement/control.rs index 10fdc96f..36c9523c 100644 --- a/llvm/src/statement/control.rs +++ b/llvm/src/statement/control.rs @@ -47,17 +47,13 @@ fn truthy_to_i1<'ctx>( iv } else { let zero = iv.get_type().const_zero(); - builder - .build_int_compare(IntPredicate::NE, iv, zero, name) - .unwrap() + builder.build_int_compare(IntPredicate::NE, iv, zero, name).unwrap() } - } + }, BasicValueEnum::FloatValue(fv) => { let zero = fv.get_type().const_float(0.0); - builder - .build_float_compare(FloatPredicate::ONE, fv, zero, name) - .unwrap() - } + builder.build_float_compare(FloatPredicate::ONE, fv, zero, name).unwrap() + }, BasicValueEnum::PointerValue(pv) => builder.build_is_not_null(pv, name).unwrap(), _ => panic!("Unsupported condition type"), } @@ -78,25 +74,16 @@ fn node_breaks_current_loop(node: &ASTNode) -> bool { match statement { StatementNode::Break => true, - StatementNode::If { - body, - else_if_blocks, - else_block, - .. - } => { + StatementNode::If { body, else_if_blocks, else_block, .. } => { block_breaks_current_loop(body) || else_if_blocks.as_ref().is_some_and(|blocks| { - blocks - .iter() - .any(|(_, block)| block_breaks_current_loop(block)) + blocks.iter().any(|(_, block)| block_breaks_current_loop(block)) }) - || else_block - .as_ref() - .is_some_and(|block| block_breaks_current_loop(block)) - } + || else_block.as_ref().is_some_and(|block| block_breaks_current_loop(block)) + }, StatementNode::Match { arms, .. } => { arms.iter().any(|arm| block_breaks_current_loop(&arm.body)) - } + }, StatementNode::While { .. } | StatementNode::For { .. } => false, _ => false, } @@ -107,14 +94,10 @@ fn eval_match_case_const<'ctx>( pattern: &MatchPattern, program: &TypedProgram, ) -> inkwell::values::IntValue<'ctx> { - let raw = program - .integer_pattern_of(pattern) - .expect("validated integer pattern"); + let raw = program.integer_pattern_of(pattern).expect("validated integer pattern"); let (negative, radix, digits) = crate::codegen::number::parse_integer(raw).expect("validated match value"); - let value = discr_ty - .const_int_from_string(&digits, radix) - .expect("validated case width"); + let value = discr_ty.const_int_from_string(&digits, radix).expect("validated case width"); if negative { value.const_neg() } else { @@ -142,11 +125,7 @@ fn gen_variant_pattern_test<'ctx>( MatchPattern::Wildcard => (context.bool_type().const_int(1, false), Vec::new()), MatchPattern::Binding(name) => ( context.bool_type().const_int(1, false), - vec![VariantBinding { - name: name.clone(), - ptr: value_ptr, - ty: value_type.clone(), - }], + vec![VariantBinding { name: name.clone(), ptr: value_ptr, ty: value_type.clone() }], ), MatchPattern::Variant { payloads, .. } => { let metadata = program.variant_pattern_of(pattern).unwrap_or_else(|| { @@ -162,16 +141,10 @@ fn gen_variant_pattern_test<'ctx>( .build_struct_gep(variant_ty, value_ptr, 0, "variant.match.tag.ptr") .unwrap(); let tag = builder - .build_load( - context.i32_type().as_basic_type_enum(), - tag_ptr, - "variant.match.tag", - ) + .build_load(context.i32_type().as_basic_type_enum(), tag_ptr, "variant.match.tag") .unwrap() .into_int_value(); - let expected_tag = context - .i32_type() - .const_int(metadata.discriminant as u64, false); + let expected_tag = context.i32_type().const_int(metadata.discriminant as u64, false); let mut condition = builder .build_int_compare(IntPredicate::EQ, tag, expected_tag, "variant.match.case") .unwrap(); @@ -204,16 +177,15 @@ fn gen_variant_pattern_test<'ctx>( payload_wave_type, struct_types, ); - condition = builder - .build_and(condition, nested_condition, "variant.match.and") - .unwrap(); + condition = + builder.build_and(condition, nested_condition, "variant.match.and").unwrap(); bindings.append(&mut nested_bindings); } (condition, bindings) - } + }, MatchPattern::Int(_) | MatchPattern::Ident(_) => { panic!("integer pattern reached variant LLVM lowering") - } + }, } } @@ -241,9 +213,8 @@ fn gen_variant_match_ir<'ctx>( let WaveType::Variant(name) = value_type else { panic!("variant match lowering received a non-variant value type"); }; - let variant_ty = *struct_types - .get(name) - .unwrap_or_else(|| panic!("variant type '{}' not found", name)); + let variant_ty = + *struct_types.get(name).unwrap_or_else(|| panic!("variant type '{}' not found", name)); let value = generate_expression_ir( program, context, @@ -258,9 +229,7 @@ fn gen_variant_match_ir<'ctx>( target_data, extern_c_info, ); - let value_ptr = builder - .build_alloca(variant_ty, "variant.match.value") - .unwrap(); + let value_ptr = builder.build_alloca(variant_ty, "variant.match.value").unwrap(); builder.build_store(value_ptr, value).unwrap(); let current_fn = builder.get_insert_block().unwrap().get_parent().unwrap(); @@ -291,20 +260,14 @@ fn gen_variant_match_ir<'ctx>( let body_block = context.append_basic_block(current_fn, &format!("variant.match.arm.{index}")); let next_block = test_blocks.get(index + 1).copied().unwrap_or(fail_block); - builder - .build_conditional_branch(condition, body_block, next_block) - .unwrap(); + builder.build_conditional_branch(condition, body_block, next_block).unwrap(); builder.position_at_end(body_block); *variables = outer_variables.clone(); for binding in bindings { variables.insert( binding.name, - VariableInfo { - ptr: binding.ptr, - mutability: Mutability::Var, - ty: binding.ty, - }, + VariableInfo { ptr: binding.ptr, mutability: Mutability::Var, ty: binding.ty }, ); } for statement in &arm.body { @@ -327,10 +290,7 @@ fn gen_variant_match_ir<'ctx>( program, ); } - if builder - .get_insert_block() - .is_some_and(|block| block.get_terminator().is_none()) - { + if builder.get_insert_block().is_some_and(|block| block.get_terminator().is_none()) { all_arms_terminate = false; builder.build_unconditional_branch(merge_block).unwrap(); } @@ -389,9 +349,7 @@ pub(super) fn gen_if_ir<'ctx>( let merge_block = context.append_basic_block(current_fn, "merge"); let mut merge_reachable = false; - builder - .build_conditional_branch(cond_i1, then_block, else_block_bb) - .unwrap(); + builder.build_conditional_branch(cond_i1, then_block, else_block_bb).unwrap(); builder.position_at_end(then_block); let mut scope_variables = variables.clone(); @@ -448,9 +406,7 @@ pub(super) fn gen_if_ir<'ctx>( let then_bb = context.append_basic_block(current_fn, "else_if_then"); let next_check_bb = context.append_basic_block(current_fn, "next_else_if"); - builder - .build_conditional_branch(c_i1, then_bb, next_check_bb) - .unwrap(); + builder.build_conditional_branch(c_i1, then_bb, next_check_bb).unwrap(); builder.position_at_end(then_bb); let mut scope_variables = variables.clone(); @@ -608,9 +564,7 @@ pub(super) fn gen_while_ir<'ctx>( ); let cond_bool = truthy_to_i1(context, builder, cond_val, "while_cond"); - builder - .build_conditional_branch(cond_bool, body_block, merge_block) - .unwrap(); + builder.build_conditional_branch(cond_bool, body_block, merge_block).unwrap(); builder.position_at_end(body_block); let mut scope_variables = variables.clone(); @@ -713,10 +667,7 @@ pub(super) fn gen_match_ir<'ctx>( let discr = match discr_any { BasicValueEnum::IntValue(iv) => iv, - other => panic!( - "match value must be integer/enum type, got {:?}", - other.get_type() - ), + other => panic!("match value must be integer/enum type, got {:?}", other.get_type()), }; let discr_ty = discr.get_type(); @@ -735,7 +686,7 @@ pub(super) fn gen_match_ir<'ctx>( panic!("duplicate wildcard match arm (`_`)"); } default_arm = Some(arm); - } + }, pat @ (MatchPattern::Int(_) | MatchPattern::Ident(_)) => { let case_value = eval_match_case_const(discr_ty, pat, program); let case_key = case_value.print_to_string().to_string(); @@ -746,10 +697,10 @@ pub(super) fn gen_match_ir<'ctx>( let case_block = context.append_basic_block(current_fn, &format!("match.case.{}", idx)); case_entries.push((case_value, case_block, arm)); - } + }, MatchPattern::Binding(_) | MatchPattern::Variant { .. } => { panic!("variant pattern reached LLVM before variant lowering"); - } + }, } } @@ -763,9 +714,7 @@ pub(super) fn gen_match_ir<'ctx>( let switch_cases: Vec<(inkwell::values::IntValue<'ctx>, BasicBlock<'ctx>)> = case_entries.iter().map(|(v, bb, _)| (*v, *bb)).collect(); - builder - .build_switch(discr, default_block, &switch_cases) - .unwrap(); + builder.build_switch(discr, default_block, &switch_cases).unwrap(); for (_, case_block, arm) in case_entries { builder.position_at_end(case_block); @@ -905,9 +854,7 @@ pub(super) fn gen_for_ir<'ctx>( ); let cond_bool = truthy_to_i1(context, builder, cond_val, "for_cond"); - builder - .build_conditional_branch(cond_bool, body_block, merge_block) - .unwrap(); + builder.build_conditional_branch(cond_bool, body_block, merge_block).unwrap(); builder.position_at_end(body_block); let mut scope_variables = variables.clone(); @@ -1009,7 +956,7 @@ pub(super) fn gen_return_ir<'ctx>( match (expected_ret, expr_opt) { (None, None) => { builder.build_return(None).unwrap(); - } + }, (None, Some(expression)) => { generate_expression_ir( @@ -1027,7 +974,7 @@ pub(super) fn gen_return_ir<'ctx>( extern_c_info, ); builder.build_return(None).unwrap(); - } + }, (Some(ret_ty), None) => { let is_i32_main = current_function.get_name().to_str().ok() == Some("main") @@ -1039,7 +986,7 @@ pub(super) fn gen_return_ir<'ctx>( } else { panic!("Non-void function must return a value"); } - } + }, (Some(ret_ty), Some(expr)) => { let mut v = generate_expression_ir( @@ -1070,6 +1017,6 @@ pub(super) fn gen_return_ir<'ctx>( } builder.build_return(Some(&v)).unwrap(); - } + }, } } diff --git a/llvm/src/statement/integer_io.rs b/llvm/src/statement/integer_io.rs index a2b10602..fc6bde47 100644 --- a/llvm/src/statement/integer_io.rs +++ b/llvm/src/statement/integer_io.rs @@ -29,18 +29,12 @@ fn install<'ctx>( ir: &str, ) -> FunctionValue<'ctx> { let buffer = MemoryBuffer::create_from_memory_range_copy(ir.as_bytes(), "integer.io"); - let helper = context - .create_module_from_ir(buffer) - .expect("valid integer I/O helper IR"); + let helper = context.create_module_from_ir(buffer).expect("valid integer I/O helper IR"); helper.set_triple(&module.get_triple()); helper.set_data_layout(&module.get_data_layout()); helper.verify().expect("valid integer I/O helper"); - module - .link_in_module(helper) - .expect("link integer I/O helper"); - let function = module - .get_function(name) - .expect("installed integer I/O helper"); + module.link_in_module(helper).expect("link integer I/O helper"); + let function = module.get_function(name).expect("installed integer I/O helper"); function.set_linkage(Linkage::Internal); function } @@ -191,22 +185,15 @@ pub(super) fn scanner<'ctx>( let negative = limit(bits, signed, true, boolean); let split = |text: &str| { ( - if text.len() == 1 { - "0".to_string() - } else { - text[..text.len() - 1].to_string() - }, + if text.len() == 1 { "0".to_string() } else { text[..text.len() - 1].to_string() }, text.as_bytes()[text.len() - 1] - b'0', ) }; let (posq, posr) = split(&positive); let (negq, negr) = split(&negative); let signed_literal = if signed { "true" } else { "false" }; - let cast = if bits > 8 { - format!("zext i8 %digit to i{bits}") - } else { - "add i8 %digit, 0".into() - }; + let cast = + if bits > 8 { format!("zext i8 %digit to i{bits}") } else { "add i8 %digit, 0".into() }; let ir = format!( r#" @{name}.first = private constant [11 x i8] c" %1[-+0-9]\00" diff --git a/llvm/src/statement/io.rs b/llvm/src/statement/io.rs index 492e45e8..51ed9bf7 100644 --- a/llvm/src/statement/io.rs +++ b/llvm/src/statement/io.rs @@ -57,17 +57,12 @@ fn printf<'ctx>( format: &[u8], args: &[BasicMetadataValueEnum<'ctx>], ) { - let ty = context - .i32_type() - .fn_type(&[context.ptr_type(AddressSpace::default()).into()], true); - let function = module - .get_function("printf") - .unwrap_or_else(|| module.add_function("printf", ty, None)); + let ty = context.i32_type().fn_type(&[context.ptr_type(AddressSpace::default()).into()], true); + let function = + module.get_function("printf").unwrap_or_else(|| module.add_function("printf", ty, None)); let mut values = vec![c_string(context, module, format).into()]; values.extend_from_slice(args); - builder - .build_call(function, &values, "printf_call") - .unwrap(); + builder.build_call(function, &values, "printf_call").unwrap(); } fn scanf<'ctx>( @@ -77,12 +72,9 @@ fn scanf<'ctx>( format: &[u8], pointer: PointerValue<'ctx>, ) -> IntValue<'ctx> { - let ty = context - .i32_type() - .fn_type(&[context.ptr_type(AddressSpace::default()).into()], true); - let function = module - .get_function("scanf") - .unwrap_or_else(|| module.add_function("scanf", ty, None)); + let ty = context.i32_type().fn_type(&[context.ptr_type(AddressSpace::default()).into()], true); + let function = + module.get_function("scanf").unwrap_or_else(|| module.add_function("scanf", ty, None)); builder .build_call( function, @@ -105,19 +97,11 @@ fn require_input<'ctx>( let function = builder.get_insert_block().unwrap().get_parent().unwrap(); let success = context.append_basic_block(function, "input.ok"); let failure = context.append_basic_block(function, "input.fail"); - builder - .build_conditional_branch(ok, success, failure) - .unwrap(); + builder.build_conditional_branch(ok, success, failure).unwrap(); builder.position_at_end(failure); - let ty = context - .void_type() - .fn_type(&[context.i32_type().into()], false); - let exit = module - .get_function("exit") - .unwrap_or_else(|| module.add_function("exit", ty, None)); - builder - .build_call(exit, &[context.i32_type().const_int(1, false).into()], "") - .unwrap(); + let ty = context.void_type().fn_type(&[context.i32_type().into()], false); + let exit = module.get_function("exit").unwrap_or_else(|| module.add_function("exit", ty, None)); + builder.build_call(exit, &[context.i32_type().const_int(1, false).into()], "").unwrap(); builder.build_unreachable().unwrap(); builder.position_at_end(success); } @@ -174,9 +158,8 @@ pub(super) fn gen_print_format_ir<'ctx>( match value { BasicValueEnum::IntValue(mut integer) => { if spec == "c" { - integer = builder - .build_int_cast(integer, context.i32_type(), "print.char") - .unwrap(); + integer = + builder.build_int_cast(integer, context.i32_type(), "print.char").unwrap(); values.push(integer.into()); formats.push("%c"); } else { @@ -196,10 +179,7 @@ pub(super) fn gen_print_format_ir<'ctx>( let buffer = build_entry_alloca( context, builder, - context - .i8_type() - .array_type(integer_io::output_capacity(bits, hex)) - .into(), + context.i8_type().array_type(integer_io::output_capacity(bits, hex)).into(), "print.integer", ); let text = builder @@ -211,34 +191,23 @@ pub(super) fn gen_print_format_ir<'ctx>( values.push(text.into()); formats.push("%s"); } - } + }, BasicValueEnum::FloatValue(number) => { - let number = builder - .build_float_cast(number, context.f64_type(), "print.double") - .unwrap(); + let number = + builder.build_float_cast(number, context.f64_type(), "print.double").unwrap(); values.push(number.into()); formats.push("%f"); - } + }, BasicValueEnum::PointerValue(pointer) => { let string = matches!(ty, WaveType::String) || matches!(ty, WaveType::Pointer(ref t) if matches!(t.as_ref(), WaveType::Byte | WaveType::Char)); values.push(pointer.into()); - formats.push(if spec == "s" || spec.is_empty() && string { - "%s" - } else { - "%p" - }); - } + formats.push(if spec == "s" || spec.is_empty() && string { "%s" } else { "%p" }); + }, _ => unreachable!("frontend validates scalar output"), } } - printf( - context, - builder, - module, - &wave_format_to_c(format, &formats), - &values, - ); + printf(context, builder, module, &wave_format_to_c(format, &formats), &values); } pub(super) fn gen_input_ir<'ctx>( @@ -293,9 +262,7 @@ pub(super) fn gen_input_ir<'ctx>( context.i32_type().into(), "input.literal.count", ); - builder - .build_store(count, context.i32_type().const_all_ones()) - .unwrap(); + builder.build_store(count, context.i32_type().const_all_ones()).unwrap(); scanf(context, builder, module, &literal, count); let read = builder .build_load(context.i32_type(), count, "input.literal.read") @@ -310,7 +277,7 @@ pub(super) fn gen_input_ir<'ctx>( ) .unwrap(); require_input(context, builder, module, ok); - } + }, FormatFragment::Placeholder(_) => { let (destination, ty) = destinations.next().expect("validated arity"); let ok = match ty { @@ -329,15 +296,15 @@ pub(super) fn gen_input_ir<'ctx>( .basic() .unwrap() .into_int_value() - } + }, WaveType::Char | WaveType::Float(_) => { let (element, format) = match ty { WaveType::Char => { (context.i8_type().as_basic_type_enum(), b"%c".as_slice()) - } + }, WaveType::Float(32) => { (context.f32_type().as_basic_type_enum(), b"%f".as_slice()) - } + }, _ => (context.f64_type().as_basic_type_enum(), b"%lf".as_slice()), }; let temporary = @@ -352,16 +319,15 @@ pub(super) fn gen_input_ir<'ctx>( ) .unwrap(); require_input(context, builder, module, ok); - let value = builder - .build_load(element, temporary, "input.scalar.value") - .unwrap(); + let value = + builder.build_load(element, temporary, "input.scalar.value").unwrap(); builder.build_store(destination, value).unwrap(); context.bool_type().const_int(1, false) - } + }, _ => unreachable!("frontend validates input destinations"), }; require_input(context, builder, module, ok); - } + }, } } } diff --git a/llvm/src/statement/mod.rs b/llvm/src/statement/mod.rs index 6cbe905f..bb9a8ffb 100644 --- a/llvm/src/statement/mod.rs +++ b/llvm/src/statement/mod.rs @@ -57,10 +57,7 @@ pub fn generate_statement_ir<'ctx>( ) { // A return, break, continue, or unconditional branch may have terminated the // block while walking a source-level list. LLVM rejects a second terminator. - if builder - .get_insert_block() - .is_some_and(|block| block.get_terminator().is_some()) - { + if builder.get_insert_block().is_some_and(|block| block.get_terminator().is_some()) { return; } @@ -79,12 +76,12 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, ASTNode::Statement(StatementNode::Println(message)) | ASTNode::Statement(StatementNode::Print(message)) => { io::gen_print_literal_ir(context, builder, module, string_counter, message); - } + }, ASTNode::Statement(StatementNode::PrintlnFormat { format, args }) | ASTNode::Statement(StatementNode::PrintFormat { format, args }) => { @@ -104,7 +101,7 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, ASTNode::Statement(StatementNode::Input { format, args }) => { io::gen_input_ir( @@ -123,14 +120,9 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, - ASTNode::Statement(StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - }) => { + ASTNode::Statement(StatementNode::If { condition, body, else_if_blocks, else_block }) => { control::gen_if_ir( context, builder, @@ -152,7 +144,7 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, ASTNode::Statement(StatementNode::While { condition, body }) => { control::gen_while_ir( @@ -174,7 +166,7 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, ASTNode::Statement(StatementNode::Match { value, arms }) => { control::gen_match_ir( @@ -196,14 +188,9 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, - ASTNode::Statement(StatementNode::For { - initialization, - condition, - increment, - body, - }) => { + ASTNode::Statement(StatementNode::For { initialization, condition, increment, body }) => { control::gen_for_ir( context, builder, @@ -225,14 +212,9 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, - ASTNode::Statement(StatementNode::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - }) => { + ASTNode::Statement(StatementNode::AsmBlock { instructions, inputs, outputs, clobbers }) => { asm::gen_asm_stmt_ir( program, context, @@ -261,7 +243,7 @@ pub fn generate_statement_ir<'ctx>( ) }, ); - } + }, ASTNode::Statement(StatementNode::Expression(expr)) => { expr_stmt::gen_expr_stmt_ir( @@ -279,16 +261,12 @@ pub fn generate_statement_ir<'ctx>( ); if matches!( program.type_of(expr), - Some(hir::HirExpressionType::Resolved( - parser::ast::WaveType::Never - )) - ) && builder - .get_insert_block() - .is_some_and(|block| block.get_terminator().is_none()) + Some(hir::HirExpressionType::Resolved(parser::ast::WaveType::Never)) + ) && builder.get_insert_block().is_some_and(|block| block.get_terminator().is_none()) { builder.build_unreachable().unwrap(); } - } + }, ASTNode::Statement(StatementNode::Assign { variable, value }) => { assign::gen_assign_ir( @@ -305,15 +283,15 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, ASTNode::Statement(StatementNode::Break) => { control::gen_break_ir(builder, loop_exit_stack); - } + }, ASTNode::Statement(StatementNode::Continue) => { control::gen_continue_ir(builder, loop_continue_stack); - } + }, ASTNode::Statement(StatementNode::Return(expr_opt)) => { control::gen_return_ir( @@ -330,8 +308,8 @@ pub fn generate_statement_ir<'ctx>( extern_c_info, program, ); - } + }, - _ => {} + _ => {}, } } diff --git a/llvm/src/statement/variable.rs b/llvm/src/statement/variable.rs index ddfb1782..518ff289 100644 --- a/llvm/src/statement/variable.rs +++ b/llvm/src/statement/variable.rs @@ -40,10 +40,7 @@ pub enum CoercionMode { } pub(crate) fn wave_type_is_unsigned(ty: Option<&WaveType>) -> bool { - matches!( - ty, - Some(WaveType::Uint(_) | WaveType::Bool | WaveType::Byte | WaveType::Char) - ) + matches!(ty, Some(WaveType::Uint(_) | WaveType::Bool | WaveType::Byte | WaveType::Char)) } pub(crate) fn expression_is_unsigned(program: &TypedProgram, expr: &Expression) -> bool { @@ -69,19 +66,12 @@ pub fn coerce_basic_value<'ctx>( if matches!(mode, CoercionMode::Implicit) { if let (BasicValueEnum::IntValue(value), BasicTypeEnum::IntType(target)) = (val, expected) { if value.get_type().get_bit_width() == 1 && target.get_bit_width() == 8 { - return builder - .build_int_z_extend(value, target, tag) - .unwrap() - .into(); + return builder.build_int_z_extend(value, target, tag).unwrap().into(); } } - if matches!( - val, - BasicValueEnum::IntValue(_) | BasicValueEnum::FloatValue(_) - ) && matches!( - expected, - BasicTypeEnum::IntType(_) | BasicTypeEnum::FloatType(_) - ) { + if matches!(val, BasicValueEnum::IntValue(_) | BasicValueEnum::FloatValue(_)) + && matches!(expected, BasicTypeEnum::IntType(_) | BasicTypeEnum::FloatType(_)) + { panic!("ICE: numeric boundary conversion missing from HIR: {tag}"); } } @@ -101,51 +91,36 @@ pub fn coerce_basic_value<'ctx>( "implicit integer narrowing is forbidden: i{} -> i{}", src_bw, dst_bw ); - } - CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => builder - .build_int_truncate(iv, dst, tag) - .unwrap() - .as_basic_value_enum(), + }, + CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => { + builder.build_int_truncate(iv, dst, tag).unwrap().as_basic_value_enum() + }, } } else if src_bw == 1 || source_unsigned { - builder - .build_int_z_extend(iv, dst, tag) - .unwrap() - .as_basic_value_enum() + builder.build_int_z_extend(iv, dst, tag).unwrap().as_basic_value_enum() } else { - builder - .build_int_s_extend(iv, dst, tag) - .unwrap() - .as_basic_value_enum() + builder.build_int_s_extend(iv, dst, tag).unwrap().as_basic_value_enum() } - } + }, // float <-> float - (BasicValueEnum::FloatValue(fv), BasicTypeEnum::FloatType(dst)) => builder - .build_float_cast(fv, dst, tag) - .unwrap() - .as_basic_value_enum(), + (BasicValueEnum::FloatValue(fv), BasicTypeEnum::FloatType(dst)) => { + builder.build_float_cast(fv, dst, tag).unwrap().as_basic_value_enum() + }, // float -> int - (BasicValueEnum::FloatValue(fv), BasicTypeEnum::IntType(dst)) => builder - .build_float_to_signed_int(fv, dst, tag) - .unwrap() - .as_basic_value_enum(), + (BasicValueEnum::FloatValue(fv), BasicTypeEnum::IntType(dst)) => { + builder.build_float_to_signed_int(fv, dst, tag).unwrap().as_basic_value_enum() + }, // int -> float (BasicValueEnum::IntValue(iv), BasicTypeEnum::FloatType(dst)) => { if source_unsigned { - builder - .build_unsigned_int_to_float(iv, dst, tag) - .unwrap() - .as_basic_value_enum() + builder.build_unsigned_int_to_float(iv, dst, tag).unwrap().as_basic_value_enum() } else { - builder - .build_signed_int_to_float(iv, dst, tag) - .unwrap() - .as_basic_value_enum() + builder.build_signed_int_to_float(iv, dst, tag).unwrap().as_basic_value_enum() } - } + }, // ptr -> ptr (bitcast) (BasicValueEnum::PointerValue(pv), BasicTypeEnum::PointerType(dst)) => builder @@ -161,34 +136,25 @@ pub fn coerce_basic_value<'ctx>( } else { panic!("Implicit int->ptr is not allowed (use explicit cast)."); } - } - CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => builder - .build_int_to_ptr(iv, dst, tag) - .unwrap() - .as_basic_value_enum(), + }, + CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => { + builder.build_int_to_ptr(iv, dst, tag).unwrap().as_basic_value_enum() + }, }, // ptr -> int (BasicValueEnum::PointerValue(pv), BasicTypeEnum::IntType(dst)) => match mode { CoercionMode::Implicit => { - panic!( - "Implicit ptr->int is not allowed during '{}' (use explicit cast).", - tag - ); - } - CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => builder - .build_ptr_to_int(pv, dst, tag) - .unwrap() - .as_basic_value_enum(), + panic!("Implicit ptr->int is not allowed during '{}' (use explicit cast).", tag); + }, + CoercionMode::Asm | CoercionMode::Explicit | CoercionMode::Abi => { + builder.build_ptr_to_int(pv, dst, tag).unwrap().as_basic_value_enum() + }, }, _ => { - panic!( - "Type mismatch: expected {:?}, got {:?}", - expected, - val.get_type() - ); - } + panic!("Type mismatch: expected {:?}, got {:?}", expected, val.get_type()); + }, } } @@ -205,13 +171,7 @@ pub(super) fn gen_variable_ir<'ctx>( extern_c_info: &HashMap>, program: &TypedProgram, ) { - let VariableNode { - name, - type_name, - initial_value, - mutability, - .. - } = var_node; + let VariableNode { name, type_name, initial_value, mutability, .. } = var_node; if matches!(initial_value, Some(Expression::Null)) && !matches!(type_name, WaveType::Pointer(_)) { @@ -229,19 +189,12 @@ pub(super) fn gen_variable_ir<'ctx>( (type_name, initial_value.as_ref()) { if values.len() != *size as usize { - panic!( - "❌ Array length mismatch: expected {}, got {}", - size, - values.len() - ); + panic!("❌ Array length mismatch: expected {}, got {}", size, values.len()); } let array_ty = match llvm_type { BasicTypeEnum::ArrayType(a) => a, - other => panic!( - "WaveType::Array must lower to LLVM array type, got {:?}", - other - ), + other => panic!("WaveType::Array must lower to LLVM array type, got {:?}", other), }; let llvm_element_type = @@ -293,11 +246,7 @@ pub(super) fn gen_variable_ir<'ctx>( variables.insert( name.clone(), - VariableInfo { - ptr: alloca, - mutability: mutability.clone(), - ty: type_name.clone(), - }, + VariableInfo { ptr: alloca, mutability: mutability.clone(), ty: type_name.clone() }, ); return; @@ -309,11 +258,7 @@ pub(super) fn gen_variable_ir<'ctx>( if asm_initializer { variables.insert( name.clone(), - VariableInfo { - ptr: alloca, - mutability: mutability.clone(), - ty: type_name.clone(), - }, + VariableInfo { ptr: alloca, mutability: mutability.clone(), ty: type_name.clone() }, ); } @@ -350,11 +295,7 @@ pub(super) fn gen_variable_ir<'ctx>( if !asm_initializer { variables.insert( name.clone(), - VariableInfo { - ptr: alloca, - mutability: mutability.clone(), - ty: type_name.clone(), - }, + VariableInfo { ptr: alloca, mutability: mutability.clone(), ty: type_name.clone() }, ); } } @@ -365,9 +306,8 @@ pub(super) fn build_entry_alloca<'ctx>( ty: BasicTypeEnum<'ctx>, name: &str, ) -> PointerValue<'ctx> { - let cur_block = builder - .get_insert_block() - .unwrap_or_else(|| panic!("build_entry_alloca: no insert block")); + let cur_block = + builder.get_insert_block().unwrap_or_else(|| panic!("build_entry_alloca: no insert block")); let func = cur_block .get_parent() .unwrap_or_else(|| panic!("build_entry_alloca: insert block has no parent function")); diff --git a/llvm/src/toolchain.rs b/llvm/src/toolchain.rs index 4b79453c..b3316b40 100644 --- a/llvm/src/toolchain.rs +++ b/llvm/src/toolchain.rs @@ -25,9 +25,7 @@ use std::path::{Path, PathBuf}; /// precedence over paths relative to the running compiler and the build-time /// fallback directory. pub fn find_bundled_linux_crt(target: &str, abi: Option<&str>, name: &str) -> Option { - bundled_linux_crt_candidates(target, abi, name) - .into_iter() - .find(|path| path.is_file()) + bundled_linux_crt_candidates(target, abi, name).into_iter().find(|path| path.is_file()) } /// Returns the highest-priority path where a bundled CRT object is expected. @@ -78,10 +76,7 @@ fn bundled_linux_crt_candidates(target: &str, abi: Option<&str>, name: &str) -> fn crt_relative_path(target: &str, abi: Option<&str>, name: &str) -> PathBuf { let mut path = PathBuf::from(target); - if matches!( - target, - "riscv64-unknown-linux-gnu" | "loongarch64-unknown-linux-gnu" - ) { + if matches!(target, "riscv64-unknown-linux-gnu" | "loongarch64-unknown-linux-gnu") { path.push(abi.unwrap_or("lp64d")); } // Accept only the final component so a caller cannot escape the target CRT diff --git a/llvm/tests/codegen_errors.rs b/llvm/tests/codegen_errors.rs index dcc898fe..1411cf9f 100644 --- a/llvm/tests/codegen_errors.rs +++ b/llvm/tests/codegen_errors.rs @@ -6,17 +6,14 @@ use llvm::diagnostic::CodegenPhase; use std::sync::atomic::{AtomicU64, Ordering}; fn program(source: &str) -> TypedProgram { - let tokens = lexer::Lexer::new_with_file(source, "backend-errors.wave") - .tokenize() - .unwrap(); + let tokens = lexer::Lexer::new_with_file(source, "backend-errors.wave").tokenize().unwrap(); TypedProgram::lower(parser::parse_syntax_with_spans(&tokens).unwrap()).unwrap() } + fn options() -> BackendOptions { - BackendOptions { - target: Some("x86_64-unknown-linux-gnu".into()), - ..Default::default() - } + BackendOptions { target: Some("x86_64-unknown-linux-gnu".into()), ..Default::default() } } + fn directory() -> std::path::PathBuf { static NEXT: AtomicU64 = AtomicU64::new(0); let dir = std::env::temp_dir().join(format!( @@ -34,26 +31,20 @@ fn target_and_asm_errors_return_without_unwinding() { let mut invalid = options(); invalid.target = Some("invalid-target".into()); assert_eq!( - unsafe { generate_ir(&valid, "", &invalid) } - .unwrap_err() - .phase, + unsafe { generate_ir(&valid, "", &invalid) }.unwrap_err().phase, CodegenPhase::Target ); invalid = options(); invalid.code_model = Some("invalid-model".into()); assert_eq!( - unsafe { generate_ir(&valid, "", &invalid) } - .unwrap_err() - .phase, + unsafe { generate_ir(&valid, "", &invalid) }.unwrap_err().phase, CodegenPhase::Target ); let invalid_asm = program("fun main() { asm { in(\"not_a_register\") 1 } }"); let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe { generate_ir(&invalid_asm, "", &options()) })); - let error = result - .expect("user-triggered failure must not unwind") - .unwrap_err(); + let error = result.expect("user-triggered failure must not unwind").unwrap_err(); assert_eq!(error.phase, CodegenPhase::Validation); assert_eq!(error.operation, "inline assembly"); assert_eq!(error.span.unwrap().file, "backend-errors.wave"); @@ -69,10 +60,7 @@ fn failed_emission_preserves_old_output_and_cleans_staging_files() { emit_codegen_file(&invalid, "", &options(), &output, CodegenFileKind::Object) } .is_err()); - assert_eq!( - std::fs::read_to_string(&output).unwrap(), - "previous artifact" - ); + assert_eq!(std::fs::read_to_string(&output).unwrap(), "previous artifact"); let valid = program("fun main() -> i32 { return 0; }"); let error = unsafe { emit_codegen_file( @@ -105,9 +93,6 @@ fn missing_linker_returns_a_typed_error_and_preserves_destination() { backend.linker = Some(dir.join("missing-linker").to_string_lossy().into_owned()); let error = link_objects(&[], output.to_str().unwrap(), &[], &[], &backend).unwrap_err(); assert_eq!(error.phase, CodegenPhase::Link); - assert_eq!( - std::fs::read_to_string(&output).unwrap(), - "previous artifact" - ); + assert_eq!(std::fs::read_to_string(&output).unwrap(), "previous artifact"); assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 1); } diff --git a/llvm/tests/message_ownership.rs b/llvm/tests/message_ownership.rs index 64127387..30b0ee72 100644 --- a/llvm/tests/message_ownership.rs +++ b/llvm/tests/message_ownership.rs @@ -37,10 +37,7 @@ fn llvm_messages_and_target_layouts_share_allocator_ownership() { // This explicit destruction is where #493 crashed on native ARM64. drop(layout); drop(target_data); - assert_eq!( - module.get_data_layout().as_str().to_str().unwrap(), - expected - ); + assert_eq!(module.get_data_layout().as_str().to_str().unwrap(), expected); let ir = module.print_to_string(); assert!(ir.to_str().unwrap().contains(expected)); drop(ir); diff --git a/llvm/tests/msvc_inputs.rs b/llvm/tests/msvc_inputs.rs index 2172dbc0..61ad0d9e 100644 --- a/llvm/tests/msvc_inputs.rs +++ b/llvm/tests/msvc_inputs.rs @@ -17,6 +17,7 @@ impl Temp { fs::create_dir_all(&p).unwrap(); Self(p) } + fn lib(&self, name: &str, machine: u16) -> PathBuf { let p = self.0.join(name); fs::create_dir_all(p.parent().unwrap()).unwrap(); @@ -34,16 +35,10 @@ fn obj(machine: u16) -> Vec { b[..2].copy_from_slice(&machine.to_le_bytes()); b } + fn member(name: &str, bytes: &[u8]) -> Vec { - let mut b = format!( - "{name:<16}{:<12}{:<6}{:<6}{:<8}{:<10}`\n", - 0, - 0, - 0, - 0, - bytes.len() - ) - .into_bytes(); + let mut b = + format!("{name:<16}{:<12}{:<6}{:<6}{:<8}{:<10}`\n", 0, 0, 0, 0, bytes.len()).into_bytes(); assert_eq!(b.len(), 60); b.extend(bytes); if b.len() % 2 != 0 { @@ -55,13 +50,9 @@ fn member(name: &str, bytes: &[u8]) -> Vec { fn rejects_wrong_machine_images_bitcode_and_truncation() { assert!(coff::inspect(&obj(0x8664), X64).is_ok()); assert!(coff::inspect(&obj(0xaa64), ARM).is_ok()); - for b in [ - obj(0xaa64), - vec![], - vec![0x64, 0x86], - b"MZfake image".to_vec(), - b"BC\xc0\xde".to_vec(), - ] { + for b in + [obj(0xaa64), vec![], vec![0x64, 0x86], b"MZfake image".to_vec(), b"BC\xc0\xde".to_vec()] + { assert!(coff::inspect(&b, X64).is_err(), "{b:?}"); } let mut b = obj(0x8664); @@ -104,9 +95,7 @@ fn archives_check_every_member_and_report_long_names() { a.extend(member("valid.obj/", &obj(0x8664))); assert!(coff::inspect(&a, X64).is_ok()); a.extend(member("/0", &obj(0xaa64))); - assert!(coff::inspect(&a, X64) - .unwrap_err() - .contains("foreign object.obj")); + assert!(coff::inspect(&a, X64).unwrap_err().contains("foreign object.obj")); for cut in [9, 20, 67, a.len() - 1] { assert!(coff::inspect(&a[..cut], X64).is_err()); } @@ -126,14 +115,8 @@ fn sdk_selects_complete_numeric_versions_and_target_architecture() { t.lib("SDK/Lib/10.0.11.0/um/arm64/kernel32.lib", 0xaa64); t.lib("VC/lib/arm64/vcruntime.lib", 0xaa64); let env = sdk::Environment::from([ - ( - "WindowsSdkDir".into(), - t.0.join("SDK").display().to_string(), - ), - ( - "VCToolsInstallDir".into(), - t.0.join("VC").display().to_string(), - ), + ("WindowsSdkDir".into(), t.0.join("SDK").display().to_string()), + ("VCToolsInstallDir".into(), t.0.join("VC").display().to_string()), ]); let p = sdk::discover(ARM, &[], &env); assert_eq!(p.len(), 3); @@ -141,9 +124,7 @@ fn sdk_selects_complete_numeric_versions_and_target_architecture() { assert!(sdk::discover(X64, &[], &env).is_empty()); let mut env = env; env.insert("WindowsSDKVersion".into(), "10.0.9.0\\".into()); - assert!(sdk::discover(ARM, &[], &env)[0] - .to_string_lossy() - .contains("10.0.9.0")); + assert!(sdk::discover(ARM, &[], &env)[0].to_string_lossy().contains("10.0.9.0")); } #[test] fn explicit_paths_precede_environment_and_wrong_architecture_fails_closed() { @@ -152,20 +133,13 @@ fn explicit_paths_precede_environment_and_wrong_architecture_fails_closed() { t.lib("ambient/thing.lib", 0xaa64); let env = sdk::Environment::from([("LIB".into(), t.0.join("ambient").display().to_string())]); let paths = sdk::discover(X64, &[t.0.join("explicit").display().to_string()], &env); - let mut args = paths - .iter() - .map(|p| format!("/LIBPATH:{}", p.display())) - .collect::>(); + let mut args = paths.iter().map(|p| format!("/LIBPATH:{}", p.display())).collect::>(); args.push("thing.lib".into()); assert!(sdk::validate_arguments(X64, &args).is_ok()); args.remove(0); - assert!(sdk::validate_arguments(X64, &args) - .unwrap_err() - .contains("machine")); + assert!(sdk::validate_arguments(X64, &args).unwrap_err().contains("machine")); args.push("missing.lib".into()); - assert!(sdk::validate_arguments(X64, &args) - .unwrap_err() - .contains("missing.lib")); + assert!(sdk::validate_arguments(X64, &args).unwrap_err().contains("missing.lib")); } #[test] fn missing_default_libraries_are_actionable_and_nodefaultlib_is_respected() { @@ -209,45 +183,25 @@ fn rejects_archive_index_offsets_and_truncated_symbol_names() { index.extend(b"symbol\0"); a.extend(member("/", &index)); a.extend(member("object.obj/", &obj(0x8664))); - assert!(coff::inspect(&a, X64) - .unwrap_err() - .contains("nonexistent member")); + assert!(coff::inspect(&a, X64).unwrap_err().contains("nonexistent member")); } #[test] fn ordinary_install_roots_and_explicit_sdk_versions_do_not_mix() { let t = Temp::new(); for (part, name) in [("um", "kernel32"), ("ucrt", "ucrt")] { - t.lib( - &format!("Program Files/Windows Kits/10/Lib/10.0.1/{part}/x64/{name}.lib"), - 0x8664, - ); + t.lib(&format!("Program Files/Windows Kits/10/Lib/10.0.1/{part}/x64/{name}.lib"), 0x8664); } - t.lib( - "Visual Studio/VC/Tools/MSVC/14.9/lib/x64/vcruntime.lib", - 0x8664, - ); - t.lib( - "Visual Studio/VC/Tools/MSVC/14.10/lib/x64/vcruntime.lib", - 0x8664, - ); + t.lib("Visual Studio/VC/Tools/MSVC/14.9/lib/x64/vcruntime.lib", 0x8664); + t.lib("Visual Studio/VC/Tools/MSVC/14.10/lib/x64/vcruntime.lib", 0x8664); let mut env = sdk::Environment::from([ - ( - "ProgramFiles(x86)".into(), - t.0.join("Program Files").display().to_string(), - ), - ( - "VSINSTALLDIR".into(), - t.0.join("Visual Studio").display().to_string(), - ), + ("ProgramFiles(x86)".into(), t.0.join("Program Files").display().to_string()), + ("VSINSTALLDIR".into(), t.0.join("Visual Studio").display().to_string()), ]); let paths = sdk::discover(X64, &[], &env); assert_eq!(paths.len(), 3); assert!(paths[2].to_string_lossy().contains("14.10")); - env.insert( - "WindowsSdkDir".into(), - t.0.join("missing SDK").display().to_string(), - ); + env.insert("WindowsSdkDir".into(), t.0.join("missing SDK").display().to_string()); assert_eq!( sdk::discover(X64, &[], &env).len(), 1, @@ -264,11 +218,7 @@ fn hybrid_archive(native_machine: u16, ec_machine: u16, native_selects_ec: bool) + member("/", &[0; 20]).len() + member("//", &[0; 8]).len(); let ec_offset = native_offset + member("native.obj/", &obj(native_machine)).len(); - let selected = if native_selects_ec { - ec_offset - } else { - native_offset - }; + let selected = if native_selects_ec { ec_offset } else { native_offset }; let mut first = 1u32.to_be_bytes().to_vec(); first.extend((selected as u32).to_be_bytes()); first.extend(b"n\0"); @@ -365,14 +315,8 @@ fn machine_neutral_sdk_alias_objects_cannot_hide_instructions_or_relocations() { #[test] fn arithmetic_builtins_discovery_keeps_target_and_sdk_order() { let t = Temp::new(); - let x64 = t.lib( - "first/lib/clang/21/lib/windows/clang_rt.builtins-x86_64.lib", - 0x8664, - ); - let arm = t.lib( - "second/lib/clang/21/lib/windows/clang_rt.builtins-aarch64.lib", - 0xaa64, - ); + let x64 = t.lib("first/lib/clang/21/lib/windows/clang_rt.builtins-x86_64.lib", 0x8664); + let arm = t.lib("second/lib/clang/21/lib/windows/clang_rt.builtins-aarch64.lib", 0xaa64); let roots = vec![t.0.join("first"), t.0.join("second")]; assert_eq!(llvm::msvc::runtime::find_builtins(X64, &roots), Some(x64)); assert_eq!(llvm::msvc::runtime::find_builtins(ARM, &roots), Some(arm)); diff --git a/llvm/tests/msvc_link_arguments.rs b/llvm/tests/msvc_link_arguments.rs index 7a558b89..fbafab5d 100644 --- a/llvm/tests/msvc_link_arguments.rs +++ b/llvm/tests/msvc_link_arguments.rs @@ -2,10 +2,9 @@ use llvm::backend::msvc_link_args; #[test] fn msvc_machine_crt_and_output_modes_are_independent() { - for (target, machine) in [ - ("x86_64-pc-windows-msvc", "X64"), - ("aarch64-pc-windows-msvc", "ARM64"), - ] { + for (target, machine) in + [("x86_64-pc-windows-msvc", "X64"), ("aarch64-pc-windows-msvc", "ARM64")] + { for static_crt in [false, true] { for shared in [false, true] { for no_default in [false, true] { diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..be6f818b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,13 @@ +edition = "2021" +style_edition = "2021" + +hard_tabs = false +max_width = 100 +newline_style = "Unix" +tab_spaces = 4 + +use_small_heuristics = "Max" + +match_block_trailing_comma = true +use_field_init_shorthand = true +use_try_shorthand = true diff --git a/src/cli.rs b/src/cli.rs index b479bd1d..53426034 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -44,11 +44,7 @@ use utils::colorex::*; #[derive(Debug)] enum CliCommand { Build(BuildRequest), - Print { - item: String, - target: Option, - format: PrintFormat, - }, + Print { item: String, target: Option, format: PrintFormat }, StdInstall(Option), StdUpdate(Option), Help, @@ -285,23 +281,21 @@ fn dispatch(mut global: Global, cmd: CliCommand) -> Result<(), CliError> { CliCommand::Version => { print_version_for_backend(global.whale.enabled); Ok(()) - } + }, CliCommand::Help => { print_help(); Ok(()) - } + }, CliCommand::Build(build) => dispatch_build(&global, &build), - CliCommand::Print { - item, - target, - format, - } => dispatch_print(&global, &item, target.as_deref(), format), + CliCommand::Print { item, target, format } => { + dispatch_print(&global, &item, target.as_deref(), format) + }, CliCommand::StdInstall(reference) => { wave_std::std_install_with_reference(reference.as_deref()) - } + }, CliCommand::StdUpdate(reference) => { wave_std::std_update_with_reference(reference.as_deref()) - } + }, } } @@ -309,10 +303,7 @@ fn dispatch(mut global: Global, cmd: CliCommand) -> Result<(), CliError> { /// LLVM target resolution: an LLVM installation must not decide Whale support. fn dispatch_whale_build(global: &Global, build: &BuildRequest) -> Result<(), CliError> { let mut global = global.clone(); - let target = global - .llvm - .target - .get_or_insert_with(|| "x86_64-unknown-linux-gnu".into()); + let target = global.llvm.target.get_or_insert_with(|| "x86_64-unknown-linux-gnu".into()); if target != "x86_64-unknown-linux-gnu" { return Err(CliError::usage( "Whale currently supports only --target x86_64-unknown-linux-gnu", @@ -387,11 +378,7 @@ fn dispatch_whale_build(global: &Global, build: &BuildRequest) -> Result<(), Cli }; let mut inputs = build.inputs.clone(); for input in &classified { - inputs.extend(runner::wave_input_paths( - &input.path, - &global.dep, - &global.llvm, - )); + inputs.extend(runner::wave_input_paths(&input.path, &global.dep, &global.llvm)); } crate::output_guard::validate(&inputs, &outputs)?; if build.dry_run { @@ -491,8 +478,8 @@ fn dispatch_build(global: &Global, build: &BuildRequest) -> Result<(), CliError> }, InputKind::Ir | InputKind::Bc | InputKind::Asm => { compile_non_wave_to_object(&effective_global, job)?; - } - InputKind::Obj | InputKind::Archive => {} + }, + InputKind::Obj | InputKind::Archive => {}, } } @@ -541,9 +528,7 @@ fn effective_global_for_build(global: &Global, build: &BuildRequest) -> Global { out.llvm.link_args.push("-nostartfiles".to_string()); } if let Some(script) = &build.linker_script { - out.llvm - .link_args - .push(format!("-Wl,-T,{}", script.to_string_lossy())); + out.llvm.link_args.push(format!("-Wl,-T,{}", script.to_string_lossy())); } out @@ -581,23 +566,23 @@ fn dispatch_print_human(global: &Global, item: &str, target: &str) -> Result<(), "host-target" | "default-target" => { println!("{}", host_target_triple()); Ok(()) - } + }, "host" => { validate_target_options_for(&host_target_triple(), &global.llvm)?; print_target_spec_human(global, &host_target_triple()); Ok(()) - } + }, "target-spec" => { validate_target_options_for(target, &global.llvm)?; print_target_spec_human(global, target); Ok(()) - } + }, "target-list" | "supported-targets" => { for t in supported_targets() { println!("{}", t); } Ok(()) - } + }, "sysroot" => { let selection = effective_sysroot_selection(global, target)?; if let Some(selection) = selection { @@ -606,7 +591,7 @@ fn dispatch_print_human(global: &Global, item: &str, target: &str) -> Result<(), println!(); } Ok(()) - } + }, "std-path" => { if let Some(path) = default_std_path(global) { println!("{}", path); @@ -614,52 +599,52 @@ fn dispatch_print_human(global: &Global, item: &str, target: &str) -> Result<(), println!(); } Ok(()) - } + }, "dep-search-paths" => { if let Some(path) = default_std_path(global) { println!("{}", path); } Ok(()) - } + }, "default-linker" => { ensure_supported_target(target)?; let target_global = global_with_target(global, target); println!("{}", default_linker_name(&target_global)); Ok(()) - } + }, "supported-input-types" => { for t in supported_input_types() { println!("{}", t); } Ok(()) - } + }, "supported-emit-kinds" => { println!("check (control-mode)"); for e in supported_artifact_emit_kinds() { println!("{}", e); } Ok(()) - } + }, "supported-print-items" => { for item in supported_print_items() { println!("{}", item); } Ok(()) - } + }, "cpu-list" => { let spec = ensure_supported_target(target)?; for cpu in spec.cpus { println!("{}", cpu); } Ok(()) - } + }, "target-features" => { let spec = ensure_supported_target(target)?; for feat in spec.features { println!("{}", feat); } Ok(()) - } + }, _ => Err(CliError::usage(format!("unknown print item: {}", item))), } } @@ -669,21 +654,21 @@ fn dispatch_print_json(global: &Global, item: &str, target: &str) -> Result<(), "host-target" | "default-target" => { println!("{}", json_string(&host_target_triple())); Ok(()) - } + }, "host" => { validate_target_options_for(&host_target_triple(), &global.llvm)?; println!("{}", target_spec_json(global, &host_target_triple())); Ok(()) - } + }, "target-spec" => { validate_target_options_for(target, &global.llvm)?; println!("{}", target_spec_json(global, target)); Ok(()) - } + }, "target-list" | "supported-targets" => { println!("{}", json_string_array(supported_targets())); Ok(()) - } + }, "sysroot" => { let selection = effective_sysroot_selection(global, target)?; println!( @@ -693,49 +678,49 @@ fn dispatch_print_json(global: &Global, item: &str, target: &str) -> Result<(), )) ); Ok(()) - } + }, "std-path" => { println!( "{}", schema_json(&Value::optional_string(default_std_path(global).as_deref())) ); Ok(()) - } + }, "dep-search-paths" => { let paths = default_std_path(global).into_iter().collect::>(); println!("{}", schema_json(&Value::strings(&paths))); Ok(()) - } + }, "default-linker" => { ensure_supported_target(target)?; let target_global = global_with_target(global, target); println!("{}", json_string(&default_linker_name(&target_global))); Ok(()) - } + }, "supported-input-types" => { println!("{}", json_string_array(supported_input_types())); Ok(()) - } + }, "supported-emit-kinds" => { let mut kinds = vec!["check"]; kinds.extend(supported_artifact_emit_kinds()); println!("{}", json_string_array(kinds)); Ok(()) - } + }, "supported-print-items" => { println!("{}", json_string_array(supported_print_items())); Ok(()) - } + }, "cpu-list" => { let spec = ensure_supported_target(target)?; println!("{}", json_string_array(spec.cpus.to_vec())); Ok(()) - } + }, "target-features" => { let spec = ensure_supported_target(target)?; println!("{}", json_string_array(spec.features.to_vec())); Ok(()) - } + }, _ => Err(CliError::usage(format!("unknown print item: {}", item))), } } @@ -806,9 +791,7 @@ fn parse_global(args: Vec) -> Result<(Global, Vec), CliError> { if let Some(mode) = a.strip_prefix("--debug-wave=") { for value in mode.split(',').map(str::trim).filter(|v| !v.is_empty()) { if !matches!(value, "tokens" | "ast" | "ir" | "mc" | "hex" | "all") { - return Err(CliError::usage(format!( - "unknown --debug-wave mode: {value}" - ))); + return Err(CliError::usage(format!("unknown --debug-wave mode: {value}"))); } } g.debug.apply(mode); @@ -822,9 +805,7 @@ fn parse_global(args: Vec) -> Result<(Global, Vec), CliError> { })?; for value in mode.split(',').map(str::trim).filter(|v| !v.is_empty()) { if !matches!(value, "tokens" | "ast" | "ir" | "mc" | "hex" | "all") { - return Err(CliError::usage(format!( - "unknown --debug-wave mode: {value}" - ))); + return Err(CliError::usage(format!("unknown --debug-wave mode: {value}"))); } } g.debug.apply(mode); @@ -839,9 +820,8 @@ fn parse_global(args: Vec) -> Result<(Global, Vec), CliError> { } if a == "--link" { - let lib = args - .get(i + 1) - .ok_or_else(|| CliError::usage("missing value: --link "))?; + let lib = + args.get(i + 1).ok_or_else(|| CliError::usage("missing value: --link "))?; g.link.libs.push(lib.to_string()); i += 2; continue; @@ -849,9 +829,8 @@ fn parse_global(args: Vec) -> Result<(Global, Vec), CliError> { if let Some(p) = a.strip_prefix("-L") { if p.is_empty() { - let path = args - .get(i + 1) - .ok_or_else(|| CliError::usage("missing value: -L "))?; + let path = + args.get(i + 1).ok_or_else(|| CliError::usage("missing value: -L "))?; g.link.paths.push(path.to_string()); i += 2; } else if let Some(native) = p.strip_prefix("native=") { @@ -946,24 +925,18 @@ fn parse_global(args: Vec) -> Result<(Global, Vec), CliError> { fn parse_dep_spec(spec: &str) -> Result { let trimmed = spec.trim(); if trimmed.is_empty() { - return Err(CliError::usage( - "invalid --dep value: expected =", - )); + return Err(CliError::usage("invalid --dep value: expected =")); } let Some((name, path)) = trimmed.split_once('=') else { - return Err(CliError::usage( - "invalid --dep value: expected =", - )); + return Err(CliError::usage("invalid --dep value: expected =")); }; let name = name.trim(); let path = path.trim(); if name.is_empty() || path.is_empty() { - return Err(CliError::usage( - "invalid --dep value: expected =", - )); + return Err(CliError::usage("invalid --dep value: expected =")); } let mut chars = name.chars(); @@ -975,15 +948,10 @@ fn parse_dep_spec(spec: &str) -> Result { }; if !valid { - return Err(CliError::usage( - "invalid --dep package name: use [A-Za-z_][A-Za-z0-9_]*", - )); + return Err(CliError::usage("invalid --dep package name: use [A-Za-z_][A-Za-z0-9_]*")); } - Ok(DepPackage { - name: name.to_string(), - path: path.to_string(), - }) + Ok(DepPackage { name: name.to_string(), path: path.to_string() }) } fn parse_llvm_backend_option( @@ -1003,9 +971,8 @@ fn parse_llvm_backend_option( return Ok(true); } if a == "--target" { - let v = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: --target "))?; + let v = + args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: --target "))?; if v.trim().is_empty() { return Err(CliError::usage("missing value: --target ")); } @@ -1024,9 +991,7 @@ fn parse_llvm_backend_option( return Ok(true); } if a == "--cpu" { - let v = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: --cpu "))?; + let v = args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: --cpu "))?; if v.trim().is_empty() { return Err(CliError::usage("missing value: --cpu ")); } @@ -1044,9 +1009,8 @@ fn parse_llvm_backend_option( return Ok(true); } if a == "--features" { - let v = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: --features "))?; + let v = + args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: --features "))?; if v.trim().is_empty() { return Err(CliError::usage("missing value: --features ")); } @@ -1064,9 +1028,7 @@ fn parse_llvm_backend_option( return Ok(true); } if a == "--abi" { - let v = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: --abi "))?; + let v = args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: --abi "))?; if v.trim().is_empty() { return Err(CliError::usage("missing value: --abi ")); } @@ -1085,9 +1047,8 @@ fn parse_llvm_backend_option( return Ok(true); } if a == "--sysroot" { - let v = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: --sysroot "))?; + let v = + args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: --sysroot "))?; if v.trim().is_empty() { return Err(CliError::usage("missing value: --sysroot ")); } @@ -1098,9 +1059,8 @@ fn parse_llvm_backend_option( } if a == "-C" { - let spec = args - .get(*i + 1) - .ok_or_else(|| CliError::usage("missing value: -C [=]"))?; + let spec = + args.get(*i + 1).ok_or_else(|| CliError::usage("missing value: -C [=]"))?; parse_llvm_codegen_spec(spec, llvm)?; *i += 2; return Ok(true); @@ -1153,7 +1113,7 @@ fn parse_llvm_codegen_spec(spec: &str, llvm: &mut LlvmFlags) -> Result<(), CliEr "unsupported -C option '{}': supported keys are linker, link-arg, link-sysroot, no-default-libs, code-model, relocation-model", key ))); - } + }, } Ok(()) @@ -1265,8 +1225,7 @@ fn parse_build(args: &[String]) -> Result { value } else { i += 1; - args.get(i) - .ok_or_else(|| CliError::usage("missing --ast-format value"))? + args.get(i).ok_or_else(|| CliError::usage("missing --ast-format value"))? }; build.ast_format = match value { "wson" => parser::ast_output::AstFormat::Wson, @@ -1281,11 +1240,11 @@ fn parse_build(args: &[String]) -> Result { "--" => { after_double_dash = true; i += 1; - } + }, "-c" => { compile_only = true; i += 1; - } + }, "-o" | "--output" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage(format!("missing value: {} ", a))); @@ -1295,7 +1254,7 @@ fn parse_build(args: &[String]) -> Result { } build.output = Some(PathBuf::from(v)); i += 2; - } + }, _ if a.starts_with("--output=") => { let v = a.trim_start_matches("--output="); if v.trim().is_empty() { @@ -1303,7 +1262,7 @@ fn parse_build(args: &[String]) -> Result { } build.output = Some(PathBuf::from(v)); i += 1; - } + }, "--out-dir" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --out-dir ")); @@ -1313,7 +1272,7 @@ fn parse_build(args: &[String]) -> Result { } build.out_dir = Some(PathBuf::from(v)); i += 2; - } + }, _ if a.starts_with("--out-dir=") => { let v = a.trim_start_matches("--out-dir="); if v.trim().is_empty() { @@ -1321,7 +1280,7 @@ fn parse_build(args: &[String]) -> Result { } build.out_dir = Some(PathBuf::from(v)); i += 1; - } + }, "--target-dir" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --target-dir ")); @@ -1331,7 +1290,7 @@ fn parse_build(args: &[String]) -> Result { } build.target_dir = Some(PathBuf::from(v)); i += 2; - } + }, _ if a.starts_with("--target-dir=") => { let v = a.trim_start_matches("--target-dir="); if v.trim().is_empty() { @@ -1339,47 +1298,47 @@ fn parse_build(args: &[String]) -> Result { } build.target_dir = Some(PathBuf::from(v)); i += 1; - } + }, "--emit" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --emit ")); }; apply_emit_spec(&mut build, &mut emit_explicit, v)?; i += 2; - } + }, _ if a.starts_with("--emit=") => { let v = a.trim_start_matches("--emit="); apply_emit_spec(&mut build, &mut emit_explicit, v)?; i += 1; - } + }, "--input-type" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --input-type ")); }; build.input_type = Some(parse_input_kind(v)?); i += 2; - } + }, _ if a.starts_with("--input-type=") => { let v = a.trim_start_matches("--input-type="); build.input_type = Some(parse_input_kind(v)?); i += 1; - } + }, "--link-only" => { build.link_only = true; i += 1; - } + }, "--run" => { build.run = true; i += 1; - } + }, "--dry-run" => { build.dry_run = true; i += 1; - } + }, "--freestanding" => { build.freestanding = true; i += 1; - } + }, "--entry" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --entry ")); @@ -1389,7 +1348,7 @@ fn parse_build(args: &[String]) -> Result { } build.entry = Some(v.clone()); i += 2; - } + }, _ if a.starts_with("--entry=") => { let v = a.trim_start_matches("--entry="); if v.trim().is_empty() { @@ -1397,7 +1356,7 @@ fn parse_build(args: &[String]) -> Result { } build.entry = Some(v.to_string()); i += 1; - } + }, "--linker-script" => { let Some(v) = args.get(i + 1) else { return Err(CliError::usage("missing value: --linker-script ")); @@ -1407,7 +1366,7 @@ fn parse_build(args: &[String]) -> Result { } build.linker_script = Some(PathBuf::from(v)); i += 2; - } + }, _ if a.starts_with("--linker-script=") => { let v = a.trim_start_matches("--linker-script="); if v.trim().is_empty() { @@ -1415,54 +1374,52 @@ fn parse_build(args: &[String]) -> Result { } build.linker_script = Some(PathBuf::from(v)); i += 1; - } + }, "--no-start-files" => { build.no_start_files = true; i += 1; - } + }, "--shared" => { build.shared = true; i += 1; - } + }, "--static" => { build.static_link = true; i += 1; - } + }, "--pie" => { if build.pie == Some(false) { return Err(CliError::usage("cannot combine --pie and --no-pie")); } build.pie = Some(true); i += 1; - } + }, "--no-pie" => { if build.pie == Some(true) { return Err(CliError::usage("cannot combine --pie and --no-pie")); } build.pie = Some(false); i += 1; - } + }, "--error-format" => { let Some(v) = args.get(i + 1) else { - return Err(CliError::usage( - "missing value: --error-format ", - )); + return Err(CliError::usage("missing value: --error-format ")); }; build.error_format = parse_error_format(v)?; i += 2; - } + }, _ if a.starts_with("--error-format=") => { let v = a.trim_start_matches("--error-format="); build.error_format = parse_error_format(v)?; i += 1; - } + }, _ if a.starts_with('-') => { return Err(CliError::usage(format!("unknown option for build: {}", a))); - } + }, _ => { build.inputs.push(PathBuf::from(a)); i += 1; - } + }, } } @@ -1480,7 +1437,7 @@ fn parse_build(args: &[String]) -> Result { match &build.emit { EmitSpec::Check => { return Err(CliError::usage("-c cannot be combined with --emit=check")); - } + }, EmitSpec::Set(set) => { if emit_explicit { if !(set.len() == 1 && set.contains(&EmitKind::Obj)) { @@ -1493,7 +1450,7 @@ fn parse_build(args: &[String]) -> Result { obj_only.insert(EmitKind::Obj); build.emit = EmitSpec::Set(obj_only); } - } + }, } } @@ -1554,37 +1511,27 @@ fn parse_print(args: &[String]) -> Result { return Err(CliError::usage(format!("unknown option for print: {}", a))); } - Ok(CliCommand::Print { - item, - target, - format, - }) + Ok(CliCommand::Print { item, target, format }) } fn parse_std_reference(args: &[String]) -> Result, CliError> { if args.first().map(String::as_str) != Some("std") { - return Err(CliError::usage( - "usage: wavec install|update std [--ref ]", - )); + return Err(CliError::usage("usage: wavec install|update std [--ref ]")); } let reference = match &args[1..] { [] => return Ok(None), [flag, value] if flag == "--ref" => value.as_str(), [value] if value.starts_with("--ref=") => &value[6..], _ => { - return Err(CliError::usage( - "usage: wavec install|update std [--ref ]", - )) - } + return Err(CliError::usage("usage: wavec install|update std [--ref ]")) + }, }; if reference.is_empty() || reference.starts_with('-') || reference.contains(':') || reference.chars().any(char::is_whitespace) { - return Err(CliError::usage( - "invalid std reference: expected a Git commit, branch or tag", - )); + return Err(CliError::usage("invalid std reference: expected a Git commit, branch or tag")); } Ok(Some(reference.to_owned())) } @@ -1616,10 +1563,7 @@ fn parse_error_format(v: &str) -> Result { match v.trim() { "human" => Ok(ErrorFormat::Human), "json" => Ok(ErrorFormat::Json), - _ => Err(CliError::usage(format!( - "invalid --error-format '{}': expected human, json", - v - ))), + _ => Err(CliError::usage(format!("invalid --error-format '{}': expected human, json", v))), } } @@ -1627,10 +1571,7 @@ fn parse_print_format(v: &str) -> Result { match v.trim() { "human" => Ok(PrintFormat::Human), "json" => Ok(PrintFormat::Json), - _ => Err(CliError::usage(format!( - "invalid --format '{}': expected human, json", - v - ))), + _ => Err(CliError::usage(format!("invalid --format '{}': expected human, json", v))), } } @@ -1680,9 +1621,7 @@ fn apply_emit_spec( } if saw_check && !set.is_empty() { - return Err(CliError::usage( - "--emit=check must be used alone (check is a control mode)", - )); + return Err(CliError::usage("--emit=check must be used alone (check is a control mode)")); } if saw_check { @@ -1691,12 +1630,12 @@ fn apply_emit_spec( EmitSpec::Set(ref existing) if existing.is_empty() => { build.emit = EmitSpec::Check; return Ok(()); - } + }, EmitSpec::Set(_) => { return Err(CliError::usage( "--emit=check cannot be combined with other emit kinds", )); - } + }, } } @@ -1705,13 +1644,13 @@ fn apply_emit_spec( } match &mut build.emit { - EmitSpec::Check => Err(CliError::usage( - "--emit=check cannot be combined with other emit kinds", - )), + EmitSpec::Check => { + Err(CliError::usage("--emit=check cannot be combined with other emit kinds")) + }, EmitSpec::Set(existing) => { existing.extend(set); Ok(()) - } + }, } } @@ -1719,10 +1658,7 @@ fn classify_inputs(build: &BuildRequest) -> Result, CliErro let mut out = Vec::with_capacity(build.inputs.len()); for input in &build.inputs { let kind = resolve_input_kind(input, build.input_type)?; - out.push(ClassifiedInput { - path: input.clone(), - kind, - }); + out.push(ClassifiedInput { path: input.clone(), kind }); } Ok(out) } @@ -1795,9 +1731,7 @@ fn validate_build_request( return Err(CliError::usage("cannot combine --shared and --static")); } if build.shared && build.pie.is_some() { - return Err(CliError::usage( - "cannot combine --shared with --pie/--no-pie in v1", - )); + return Err(CliError::usage("cannot combine --shared with --pie/--no-pie in v1")); } if let Some(reloc) = global.llvm.relocation_model.as_deref() { @@ -1820,14 +1754,10 @@ fn validate_build_request( if build.emit.is_check() { if build.link_only { - return Err(CliError::usage( - "--emit=check cannot be combined with --link-only", - )); + return Err(CliError::usage("--emit=check cannot be combined with --link-only")); } if build.run { - return Err(CliError::usage( - "--emit=check cannot be combined with --run", - )); + return Err(CliError::usage("--emit=check cannot be combined with --run")); } if build.output.is_some() || build.out_dir.is_some() { return Err(CliError::usage( @@ -1835,9 +1765,7 @@ fn validate_build_request( )); } if classified.iter().any(|i| i.kind != InputKind::Wave) { - return Err(CliError::usage( - "--emit=check currently supports only Wave source inputs", - )); + return Err(CliError::usage("--emit=check currently supports only Wave source inputs")); } return Ok(()); } @@ -1846,9 +1774,7 @@ fn validate_build_request( for kind in [EmitKind::Ast, EmitKind::Ir, EmitKind::Bc, EmitKind::Asm] { if emit_set.contains(&kind) - && !classified - .iter() - .any(|input| supports_emit_for_input(kind, input.kind)) + && !classified.iter().any(|input| supports_emit_for_input(kind, input.kind)) { return Err(CliError::usage(format!( "--emit={} has no compatible inputs in this build request", @@ -1859,9 +1785,7 @@ fn validate_build_request( if build.link_only { if !(emit_set.len() == 1 && emit_set.contains(&EmitKind::Bin)) { - return Err(CliError::usage( - "--link-only supports only --emit=bin in v1", - )); + return Err(CliError::usage("--link-only supports only --emit=bin in v1")); } if classified.iter().any(|i| !i.kind.is_link_input()) { return Err(CliError::usage( @@ -1881,14 +1805,10 @@ fn validate_build_request( if build.run { if !emit_set.contains(&EmitKind::Bin) { - return Err(CliError::usage( - "--run requires a binary output (emit includes bin)", - )); + return Err(CliError::usage("--run requires a binary output (emit includes bin)")); } if build.shared { - return Err(CliError::usage( - "--run is not allowed when --shared is specified", - )); + return Err(CliError::usage("--run is not allowed when --shared is specified")); } } @@ -1905,9 +1825,7 @@ fn validate_build_request( )); } if build.no_start_files && !global.llvm.no_default_libs { - return Err(CliError::usage( - "MSVC --no-start-files requires -Cno-default-libs", - )); + return Err(CliError::usage("MSVC --no-start-files requires -Cno-default-libs")); } } @@ -1932,10 +1850,7 @@ fn validate_build_request( } if build.output.is_some() { - let compile_count = classified - .iter() - .filter(|i| !i.kind.is_link_input()) - .count(); + let compile_count = classified.iter().filter(|i| !i.kind.is_link_input()).count(); let has_bin = emit_set.contains(&EmitKind::Bin) || build.run; if !has_bin { @@ -1992,11 +1907,7 @@ fn validate_output_paths( // emit or object job writes an artifact, including the final link output. if !build.dry_run { for input in inputs.iter().filter(|input| input.kind == InputKind::Wave) { - sources.extend(runner::wave_input_paths( - &input.path, - &global.dep, - &global.llvm, - )); + sources.extend(runner::wave_input_paths(&input.path, &global.dep, &global.llvm)); } crate::output_guard::validate(&sources, &outputs)?; } @@ -2034,18 +1945,14 @@ fn create_build_plan( return Ok(BuildPlan::default()); } - let compile_total = classified - .iter() - .filter(|i| !i.kind.is_link_input()) - .count(); + let compile_total = classified.iter().filter(|i| !i.kind.is_link_input()).count(); let mut compile_index = 0usize; let mut plan = BuildPlan::default(); for input in classified { if input.kind.is_link_input() { - plan.link_inputs - .push(input.path.to_string_lossy().to_string()); + plan.link_inputs.push(input.path.to_string_lossy().to_string()); continue; } @@ -2063,11 +1970,7 @@ fn create_build_plan( ); plan.link_inputs.push(output.to_string_lossy().to_string()); - plan.compile_jobs.push(CompileJob { - input: input.path.clone(), - kind: input.kind, - output, - }); + plan.compile_jobs.push(CompileJob { input: input.path.clone(), kind: input.kind, output }); compile_index += 1; } @@ -2149,11 +2052,8 @@ fn resolve_binary_output_path( } fn object_file_name(path: &Path, compile_index: usize, compile_total: usize) -> String { - let stem = path - .file_stem() - .and_then(|s| s.to_str()) - .filter(|s| !s.is_empty()) - .unwrap_or("input"); + let stem = + path.file_stem().and_then(|s| s.to_str()).filter(|s| !s.is_empty()).unwrap_or("input"); if compile_total > 1 { format!("{}_{}.o", stem, compile_index + 1) @@ -2178,10 +2078,9 @@ fn supports_emit_for_input(kind: EmitKind, input: InputKind) -> bool { EmitKind::Ast => input == InputKind::Wave, EmitKind::Ir => input == InputKind::Wave || input == InputKind::Ir, EmitKind::Bc => matches!(input, InputKind::Wave | InputKind::Ir | InputKind::Bc), - EmitKind::Asm => matches!( - input, - InputKind::Wave | InputKind::Ir | InputKind::Bc | InputKind::Asm - ), + EmitKind::Asm => { + matches!(input, InputKind::Wave | InputKind::Ir | InputKind::Bc | InputKind::Asm) + }, EmitKind::Obj => matches!( input, InputKind::Wave | InputKind::Ir | InputKind::Bc | InputKind::Asm | InputKind::Obj @@ -2215,17 +2114,11 @@ fn emit_artifact_file_name( input_total: usize, kind: EmitKind, ) -> String { - let stem = path - .file_stem() - .and_then(|s| s.to_str()) - .filter(|s| !s.is_empty()) - .unwrap_or("input"); + let stem = + path.file_stem().and_then(|s| s.to_str()).filter(|s| !s.is_empty()).unwrap_or("input"); - let base = if input_total > 1 { - format!("{}_{}", stem, input_index + 1) - } else { - stem.to_string() - }; + let base = + if input_total > 1 { format!("{}_{}", stem, input_index + 1) } else { stem.to_string() }; let ext = emit_artifact_extension(kind); if ext.is_empty() { @@ -2244,11 +2137,8 @@ fn resolve_extra_emit_output_path( ) -> PathBuf { let mut file_name = emit_artifact_file_name(&input.path, input_index, input_total, kind); if kind == EmitKind::Ast { - file_name = format!( - "{}.{}", - file_name.strip_suffix(".ast").unwrap(), - build.ast_format.extension() - ); + file_name = + format!("{}.{}", file_name.strip_suffix(".ast").unwrap(), build.ast_format.extension()); } if let Some(out_dir) = &build.out_dir { return out_dir.join(&file_name); @@ -2299,7 +2189,7 @@ fn execute_explicit_emit_artifacts( ) }; fs::write(output, text.map_err(|e| CliError::usage(e.to_string()))?)?; - } + }, EmitKind::Ir => match input.kind { InputKind::Wave => { let text = unsafe { @@ -2312,9 +2202,9 @@ fn execute_explicit_emit_artifacts( ) }; fs::write(output, text)?; - } + }, InputKind::Ir => copy_if_different(&input.path, &output)?, - _ => {} + _ => {}, }, EmitKind::Bc => match input.kind { InputKind::Wave => unsafe { @@ -2335,9 +2225,9 @@ fn execute_explicit_emit_artifacts( &output, EmitKind::Bc, )?; - } + }, InputKind::Bc => copy_if_different(&input.path, &output)?, - _ => {} + _ => {}, }, EmitKind::Asm => match input.kind { InputKind::Wave => unsafe { @@ -2358,11 +2248,11 @@ fn execute_explicit_emit_artifacts( &output, EmitKind::Asm, )?; - } + }, InputKind::Asm => copy_if_different(&input.path, &output)?, - _ => {} + _ => {}, }, - _ => {} + _ => {}, } } } @@ -2397,12 +2287,8 @@ fn compile_lowering_with_llvm_tools( return pending.commit().map_err(CliError::from); } - let stderr = String::from_utf8_lossy(&process_output.stderr) - .trim() - .to_string(); - let stdout = String::from_utf8_lossy(&process_output.stdout) - .trim() - .to_string(); + let stderr = String::from_utf8_lossy(&process_output.stderr).trim().to_string(); + let stdout = String::from_utf8_lossy(&process_output.stdout).trim().to_string(); Err(CodegenError::new( CodegenPhase::Tool, @@ -2433,15 +2319,12 @@ fn build_llvm_lowering_args( output.to_string_lossy().to_string(), ]; (resolve_bundled_tool("llvm-as"), args) - } + }, (InputKind::Ir | InputKind::Bc, EmitKind::Obj | EmitKind::Asm) => { build_llc_lowering_args(global, input, output, emit_kind) - } + }, (InputKind::Asm, EmitKind::Obj) => build_llvm_mc_lowering_args(global, input, output), - _ => ( - resolve_bundled_tool("llvm-as"), - vec!["--version".to_string()], - ), + _ => (resolve_bundled_tool("llvm-as"), vec!["--version".to_string()]), } } @@ -2590,15 +2473,11 @@ fn link_objects( configure_bundled_llvm_tool_env(&mut command, &bin); let transport = if llvm::backend::is_windows_msvc_target(&target) { - Some(llvm::msvc::response::LinkArguments::prepare( - &bin, &args, output, - )?) + Some(llvm::msvc::response::LinkArguments::prepare(&bin, &args, output)?) } else { None }; - let args = transport - .as_ref() - .map_or(&args, |transport| &transport.arguments); + let args = transport.as_ref().map_or(&args, |transport| &transport.arguments); let out = command.args(args).output().map_err(|error| { CodegenError::tool_launch( CodegenPhase::Link, @@ -2659,16 +2538,10 @@ fn validate_default_elf_runtime(global: &Global, build: &BuildRequest) -> Result missing.push(format!("target CRT ({}, crti.o, crtn.o)", start_name)); } } - let libc_names: &[&str] = if build.static_link { - &["libc.a"] - } else { - &["libc.so", "libc.a", "libc.so.6"] - }; - let libm_names: &[&str] = if build.static_link { - &["libm.a"] - } else { - &["libm.so", "libm.a", "libm.so.6"] - }; + let libc_names: &[&str] = + if build.static_link { &["libc.a"] } else { &["libc.so", "libc.a", "libc.so.6"] }; + let libm_names: &[&str] = + if build.static_link { &["libm.a"] } else { &["libm.so", "libm.a", "libm.so.6"] }; if find_elf_runtime_file_any(&target, global, libc_names).is_none() { missing.push("libc".to_string()); } @@ -2678,14 +2551,12 @@ fn validate_default_elf_runtime(global: &Global, build: &BuildRequest) -> Result if !build.static_link && !build.shared { match elf_dynamic_linker(&target, global.llvm.abi.as_deref()) { Some(loader) => { - let loader_name = Path::new(loader) - .file_name() - .and_then(|name| name.to_str()) - .unwrap_or(loader); + let loader_name = + Path::new(loader).file_name().and_then(|name| name.to_str()).unwrap_or(loader); if find_elf_runtime_file(&target, global, loader_name).is_none() { missing.push(format!("dynamic loader ({loader})")); } - } + }, None => missing.push(format!( "dynamic loader for ABI {}", global.llvm.abi.as_deref().unwrap_or("default") @@ -2733,11 +2604,7 @@ fn build_linker_args( ); llvm::msvc::runtime::add_builtins(&target, &mut args); return ( - global - .llvm - .linker - .clone() - .unwrap_or_else(|| resolve_bundled_tool("lld-link")), + global.llvm.linker.clone().unwrap_or_else(|| resolve_bundled_tool("lld-link")), args, ); } @@ -2807,11 +2674,8 @@ fn build_execute_command( match codegen { Some(target @ (CodegenTarget::Wasm32Unknown | CodegenTarget::Wasm64Unknown)) => { let mut args = vec!["--no-warnings".to_string()]; - let runner = wasm_runner( - target == CodegenTarget::Wasm64Unknown, - false, - global.error_format, - ); + let runner = + wasm_runner(target == CodegenTarget::Wasm64Unknown, false, global.error_format); args.extend([ "--input-type=module".to_string(), "--eval".to_string(), @@ -2820,7 +2684,7 @@ fn build_execute_command( ]); args.extend(build.run_args.iter().cloned()); ("node".to_string(), args) - } + }, Some(CodegenTarget::Wasm32WasiP1) => { let mut args = vec![ "--no-warnings".to_string(), @@ -2831,7 +2695,7 @@ fn build_execute_command( ]; args.extend(build.run_args.iter().cloned()); ("node".to_string(), args) - } + }, Some(CodegenTarget::LinuxLoongArch64) if std::env::consts::ARCH != "loongarch64" => { let mut args = Vec::new(); if let Some(sysroot) = &global.llvm.sysroot { @@ -2841,7 +2705,7 @@ fn build_execute_command( args.push(output.to_string_lossy().to_string()); args.extend(build.run_args.iter().cloned()); ("qemu-loongarch64".to_string(), args) - } + }, _ => (output.to_string_lossy().to_string(), build.run_args.clone()), } } @@ -2901,12 +2765,7 @@ fn build_darwin_lld_args( args.push(macos_version); let detected_sysroot = detect_macos_sysroot_owned(); - if let Some(sysroot) = global - .llvm - .sysroot - .as_deref() - .or(detected_sysroot.as_deref()) - { + if let Some(sysroot) = global.llvm.sysroot.as_deref().or(detected_sysroot.as_deref()) { args.push("-syslibroot".to_string()); args.push(sysroot.to_string()); } @@ -3110,9 +2969,7 @@ fn append_lld_link_args(args: &mut Vec, link_args: &[String]) { } if let Some(rest) = arg.strip_prefix("-Wl,") { args.extend( - rest.split(',') - .filter(|part| !part.is_empty()) - .map(|part| part.to_string()), + rest.split(',').filter(|part| !part.is_empty()).map(|part| part.to_string()), ); } else { args.push(arg.clone()); @@ -3121,11 +2978,7 @@ fn append_lld_link_args(args: &mut Vec, link_args: &[String]) { } fn target_triple_for_global(global: &Global) -> String { - global - .llvm - .target - .clone() - .unwrap_or_else(host_target_triple) + global.llvm.target.clone().unwrap_or_else(host_target_triple) } fn is_darwin_target(target: &str) -> bool { @@ -3263,22 +3116,8 @@ fn append_elf_end_files(args: &mut Vec, target: &str, global: &Global) { } fn append_elf_default_libs(args: &mut Vec, target: &str, global: &Global) { - append_elf_default_lib( - args, - target, - global, - "c", - &["libc.so", "libc.a"], - &["libc.so.6"], - ); - append_elf_default_lib( - args, - target, - global, - "m", - &["libm.so", "libm.a"], - &["libm.so.6"], - ); + append_elf_default_lib(args, target, global, "c", &["libc.so", "libc.a"], &["libc.so.6"]); + append_elf_default_lib(args, target, global, "m", &["libm.so", "libm.a"], &["libm.so.6"]); } fn append_elf_default_lib( @@ -3560,11 +3399,7 @@ fn llvm_tool_search_dirs() -> Vec { } fn linker_tool_name(bin: &str) -> String { - Path::new(bin) - .file_name() - .and_then(|name| name.to_str()) - .unwrap_or(bin) - .to_string() + Path::new(bin).file_name().and_then(|name| name.to_str()).unwrap_or(bin).to_string() } fn missing_linker_tool_name(_global: &Global, bin: &str) -> String { @@ -3633,17 +3468,13 @@ fn dry_run_explicit_emit_steps( input.path.display(), output.display() ) - } + }, (EmitKind::Ir, InputKind::Wave) => { - format!( - "[wave frontend] {} -> {} (ir)", - input.path.display(), - output.display() - ) - } + format!("[wave frontend] {} -> {} (ir)", input.path.display(), output.display()) + }, (EmitKind::Ir, InputKind::Ir) => { format!("cp {} {}", input.path.display(), output.display()) - } + }, (EmitKind::Bc, InputKind::Wave) | (EmitKind::Asm, InputKind::Wave) => { format!( "[wave frontend + LLVM] {} -> {} ({})", @@ -3651,17 +3482,17 @@ fn dry_run_explicit_emit_steps( output.display(), emit_kind_name(kind) ) - } + }, (EmitKind::Bc, InputKind::Ir) | (EmitKind::Asm, InputKind::Ir) | (EmitKind::Asm, InputKind::Bc) => { let (bin, args) = build_llvm_lowering_args(global, &input.path, input.kind, &output, kind); shell_join(&bin, &args) - } + }, (EmitKind::Bc, InputKind::Bc) | (EmitKind::Asm, InputKind::Asm) => { format!("cp {} {}", input.path.display(), output.display()) - } + }, _ => continue, }; steps.push(step); @@ -3699,14 +3530,8 @@ fn print_dry_run_human( println!(" features: {}", target_options.features); println!(" abi: {}", target_options.abi.as_deref().unwrap_or("")); println!(" isa: {}", target_options.isa.as_deref().unwrap_or("")); - println!( - " sysroot: {}", - global.llvm.sysroot.as_deref().unwrap_or("") - ); - println!( - " sysroot-source: {}", - global.llvm.sysroot_source.as_deref().unwrap_or("") - ); + println!(" sysroot: {}", global.llvm.sysroot.as_deref().unwrap_or("")); + println!(" sysroot-source: {}", global.llvm.sysroot_source.as_deref().unwrap_or("")); println!(" emit: {}", render_emit_spec(&build.emit)); println!(" link-only: {}", build.link_only); println!(" run: {}", build.run); @@ -3765,10 +3590,7 @@ fn print_dry_run_human( if let Some(link_output) = &plan.link_output { println!(" link:"); - println!( - " - {}", - render_link_command(global, build, &plan.link_inputs, link_output) - ); + println!(" - {}", render_link_command(global, build, &plan.link_inputs, link_output)); } if build.run { @@ -3859,24 +3681,12 @@ fn print_dry_run_json( ("features", Value::string(&target_options.features)), ("abi", Value::optional_string(target_options.abi.as_deref())), ("isa", Value::optional_string(target_options.isa.as_deref())), - ( - "sysroot", - Value::optional_string(global.llvm.sysroot.as_deref()), - ), - ( - "sysroot_source", - Value::optional_string(global.llvm.sysroot_source.as_deref()), - ), + ("sysroot", Value::optional_string(global.llvm.sysroot.as_deref())), + ("sysroot_source", Value::optional_string(global.llvm.sysroot_source.as_deref())), ("emit", Value::string(render_emit_spec(&build.emit))), ("emit_kinds", emit_kinds), - ( - "control_mode", - Value::optional_string(build.emit.is_check().then_some("check")), - ), - ( - "forced_input_type", - Value::optional_string(build.input_type.map(|k| k.as_str())), - ), + ("control_mode", Value::optional_string(build.emit.is_check().then_some("check"))), + ("forced_input_type", Value::optional_string(build.input_type.map(|k| k.as_str()))), ("link_only", Value::Bool(build.link_only)), ("run", Value::Bool(build.run)), ("freestanding", Value::Bool(build.freestanding)), @@ -3886,10 +3696,7 @@ fn print_dry_run_json( "linker_script", Value::optional_string(build.linker_script.as_ref().map(|p| p.to_string_lossy())), ), - ( - "out_dir", - Value::optional_string(build.out_dir.as_ref().map(|p| p.to_string_lossy())), - ), + ("out_dir", Value::optional_string(build.out_dir.as_ref().map(|p| p.to_string_lossy()))), ( "target_dir", Value::optional_string(build.target_dir.as_ref().map(|p| p.to_string_lossy())), @@ -3909,10 +3716,7 @@ fn print_dry_run_json( .collect(), ), ), - ( - "emit_jobs", - Value::strings(dry_run_explicit_emit_steps(global, build, classified)), - ), + ("emit_jobs", Value::strings(dry_run_explicit_emit_steps(global, build, classified))), ("compile", compile), ("link", link), ("execute", execute), @@ -4017,10 +3821,7 @@ fn host_target_triple() -> String { } fn supported_targets() -> Vec<&'static str> { - supported_target_specs() - .into_iter() - .map(|spec| spec.triple) - .collect() + supported_target_specs().into_iter().map(|spec| spec.triple).collect() } fn supported_input_types() -> Vec<&'static str> { @@ -4119,19 +3920,10 @@ fn print_target_spec_human(global: &Global, target: &str) { println!("freestanding: {}", !spec.hosted); println!("supported: {}", spec.supported); println!("default-linker: {}", default_linker_name(&target_global)); - println!( - "sysroot: {}", - sysroot - .as_ref() - .map(|value| value.path.as_str()) - .unwrap_or("") - ); + println!("sysroot: {}", sysroot.as_ref().map(|value| value.path.as_str()).unwrap_or("")); println!( "sysroot-source: {}", - sysroot - .as_ref() - .map(|value| value.source.as_str()) - .unwrap_or("") + sysroot.as_ref().map(|value| value.source.as_str()).unwrap_or("") ); } @@ -4154,18 +3946,9 @@ fn target_spec_json(global: &Global, target: &str) -> String { ("hosted", Value::Bool(spec.hosted)), ("freestanding", Value::Bool(!spec.hosted)), ("supported", Value::Bool(spec.supported)), - ( - "default_linker", - Value::string(default_linker_name(&target_global)), - ), - ( - "sysroot", - Value::optional_string(sysroot.as_ref().map(|v| v.path.as_str())), - ), - ( - "sysroot_source", - Value::optional_string(sysroot.as_ref().map(|v| v.source.as_str())), - ), + ("default_linker", Value::string(default_linker_name(&target_global))), + ("sysroot", Value::optional_string(sysroot.as_ref().map(|v| v.path.as_str()))), + ("sysroot_source", Value::optional_string(sysroot.as_ref().map(|v| v.source.as_str()))), ])) } @@ -4197,13 +3980,8 @@ fn ensure_supported_target(target: &str) -> Result<&'static TargetSpec, CliError fn target_options_for(target: &str, llvm: &LlvmFlags) -> Result { let spec = ensure_supported_target(target)?; - resolve_target_options( - spec, - llvm.cpu.as_deref(), - llvm.features.as_deref(), - llvm.abi.as_deref(), - ) - .map_err(CliError::usage) + resolve_target_options(spec, llvm.cpu.as_deref(), llvm.features.as_deref(), llvm.abi.as_deref()) + .map_err(CliError::usage) } fn resolve_target_configuration(llvm: &mut LlvmFlags) -> Result<(), CliError> { @@ -4213,11 +3991,7 @@ fn resolve_target_configuration(llvm: &mut LlvmFlags) -> Result<(), CliError> { .ok_or_else(|| CliError::usage("target resolution did not produce a target triple"))?; let effective = target_options_for(&target, llvm)?; llvm.cpu = Some(effective.cpu); - llvm.features = if effective.features.is_empty() { - None - } else { - Some(effective.features) - }; + llvm.features = if effective.features.is_empty() { None } else { Some(effective.features) }; llvm.abi = effective.abi; llvm.isa = effective.isa; Ok(()) @@ -4256,11 +4030,7 @@ fn effective_sysroot_selection( if let Some(path) = global.llvm.sysroot.as_ref() { return Ok(Some(SysrootSelection { path: path.clone(), - source: global - .llvm - .sysroot_source - .clone() - .unwrap_or_else(|| "explicit".to_string()), + source: global.llvm.sysroot_source.clone().unwrap_or_else(|| "explicit".to_string()), })); } Ok(detect_default_sysroot(target, effective.abi.as_deref())) @@ -4268,10 +4038,8 @@ fn effective_sysroot_selection( fn detect_default_sysroot(target: &str, abi: Option<&str>) -> Option { if is_darwin_target(target) { - return detect_macos_sysroot_owned().map(|path| SysrootSelection { - path, - source: "xcrun".to_string(), - }); + return detect_macos_sysroot_owned() + .map(|path| SysrootSelection { path, source: "xcrun".to_string() }); } if matches!( @@ -4405,13 +4173,11 @@ fn loongarch64_linux_sysroot_is_complete(target: &str, abi: Option<&str>, root: else { return false; }; - ["libc.so.6", "libm.so.6", loader_name] - .into_iter() - .all(|name| { - find_elf_runtime_file(target, &global, name).is_some_and(|path| { - loongarch64_elf_header_matches(Path::new(&path), expected_abi_flags) - }) + ["libc.so.6", "libm.so.6", loader_name].into_iter().all(|name| { + find_elf_runtime_file(target, &global, name).is_some_and(|path| { + loongarch64_elf_header_matches(Path::new(&path), expected_abi_flags) }) + }) } fn command_stdout_path(tool: &str, argument: &str) -> Option { @@ -4474,13 +4240,10 @@ fn riscv64_linux_sysroot_is_complete(target: &str, abi: Option<&str>, root: &Pat else { return false; }; - ["libc.so.6", "libm.so.6", loader_name] - .into_iter() - .all(|name| { - find_elf_runtime_file(target, &global, name).is_some_and(|path| { - riscv64_elf_header_matches(Path::new(&path), expected_abi_flags) - }) - }) + ["libc.so.6", "libm.so.6", loader_name].into_iter().all(|name| { + find_elf_runtime_file(target, &global, name) + .is_some_and(|path| riscv64_elf_header_matches(Path::new(&path), expected_abi_flags)) + }) } fn riscv64_abi_elf_flags(abi: Option<&str>) -> Option { @@ -4562,12 +4325,7 @@ pub fn print_version() { fn print_version_for_backend(whale: bool) { let os = format!("({})", get_os_pretty_name()).color("117,117,117"); - println!( - "{} {} {}", - "wavec".color("2,161,47"), - version::version().color("2,161,47"), - os - ); + println!("{} {} {}", "wavec".color("2,161,47"), version::version().color("2,161,47"), os); if whale { println!(" backend: Whale IR (experimental, Linux amd64, O0)"); @@ -4588,11 +4346,7 @@ pub fn print_help() { "build ".color("38,139,235"), "Build/check/link/run pipeline (flag-driven)" ); - println!( - " {:<22} {}", - "check ".color("38,139,235"), - "Alias: build --emit=check" - ); + println!(" {:<22} {}", "check ".color("38,139,235"), "Alias: build --emit=check"); println!( " {:<22} {}", "run ".color("38,139,235"), @@ -4613,11 +4367,7 @@ pub fn print_help() { "update std".color("38,139,235"), "Restore compiler-pinned std; --ref selects a compatible revision" ); - println!( - " {:<22} {}", - "--version".color("38,139,235"), - "Show version" - ); + println!(" {:<22} {}", "--version".color("38,139,235"), "Show version"); println!(" {:<22} {}", "--help".color("38,139,235"), "Show help"); println!("\nBackend selection:"); @@ -4675,11 +4425,7 @@ pub fn print_help() { "--no-start-files".color("38,139,235"), "Pass -nostartfiles to linker (link stage only)" ); - println!( - " {:<24} {}", - "-o ".color("38,139,235"), - "Output file" - ); + println!(" {:<24} {}", "-o ".color("38,139,235"), "Output file"); println!( " {:<24} {}", "--out-dir ".color("38,139,235"), @@ -4690,16 +4436,8 @@ pub fn print_help() { "--target-dir ".color("38,139,235"), "Intermediate/default artifact root" ); - println!( - " {:<24} {}", - "--dry-run".color("38,139,235"), - "Plan only, no compile/link/exec" - ); - println!( - " {:<24} {}", - "--error-format=...".color("38,139,235"), - "human, json" - ); + println!(" {:<24} {}", "--dry-run".color("38,139,235"), "Plan only, no compile/link/exec"); + println!(" {:<24} {}", "--error-format=...".color("38,139,235"), "human, json"); println!("\nLink mode options:"); println!( @@ -4707,53 +4445,21 @@ pub fn print_help() { "--shared".color("38,139,235"), "Build shared output (conflicts with --run)" ); - println!( - " {:<24} {}", - "--static".color("38,139,235"), - "Request static link mode" - ); - println!( - " {:<24} {}", - "--pie".color("38,139,235"), - "Enable PIE mode" - ); - println!( - " {:<24} {}", - "--no-pie".color("38,139,235"), - "Disable PIE mode" - ); + println!(" {:<24} {}", "--static".color("38,139,235"), "Request static link mode"); + println!(" {:<24} {}", "--pie".color("38,139,235"), "Enable PIE mode"); + println!(" {:<24} {}", "--no-pie".color("38,139,235"), "Disable PIE mode"); println!("\nGlobal options:"); - println!( - " {:<24} {}", - "-O0..-O3/-Os/-Oz/-Ofast".color("38,139,235"), - "Optimization level" - ); - println!( - " {:<24} {}", - "--debug-wave=...".color("38,139,235"), - "tokens,ast,ir,mc,hex,all" - ); - println!( - " {:<24} {}", - "--link=".color("38,139,235"), - "Link library" - ); - println!( - " {:<24} {}", - "-L ".color("38,139,235"), - "Library search path" - ); + println!(" {:<24} {}", "-O0..-O3/-Os/-Oz/-Ofast".color("38,139,235"), "Optimization level"); + println!(" {:<24} {}", "--debug-wave=...".color("38,139,235"), "tokens,ast,ir,mc,hex,all"); + println!(" {:<24} {}", "--link=".color("38,139,235"), "Link library"); + println!(" {:<24} {}", "-L ".color("38,139,235"), "Library search path"); println!( " {:<24} {}", "--std-root=".color("38,139,235"), "Explicit standard-library root (no fallback)" ); - println!( - " {:<24} {}", - "--dep-root=".color("38,139,235"), - "Dependency root directory" - ); + println!(" {:<24} {}", "--dep-root=".color("38,139,235"), "Dependency root directory"); println!( " {:<24} {}", "--dep==".color("38,139,235"), @@ -4761,26 +4467,10 @@ pub fn print_help() { ); println!("\nLLVM/backend options:"); - println!( - " {:<24} {}", - "--target=".color("38,139,235"), - "Target triple" - ); - println!( - " {:<24} {}", - "--cpu=".color("38,139,235"), - "Target CPU" - ); - println!( - " {:<24} {}", - "--features=".color("38,139,235"), - "Target features" - ); - println!( - " {:<24} {}", - "--abi=".color("38,139,235"), - "Target ABI" - ); + println!(" {:<24} {}", "--target=".color("38,139,235"), "Target triple"); + println!(" {:<24} {}", "--cpu=".color("38,139,235"), "Target CPU"); + println!(" {:<24} {}", "--features=".color("38,139,235"), "Target features"); + println!(" {:<24} {}", "--abi=".color("38,139,235"), "Target ABI"); println!( " {:<24} {}", "--sysroot=".color("38,139,235"), @@ -4791,11 +4481,7 @@ pub fn print_help() { "-C linker=".color("38,139,235"), "Override linker executable (default: bundled LLD)" ); - println!( - " {:<24} {}", - "-C link-arg=".color("38,139,235"), - "Append raw linker argument" - ); + println!(" {:<24} {}", "-C link-arg=".color("38,139,235"), "Append raw linker argument"); println!( " {:<24} {}", "-C link-sysroot=".color("38,139,235"), @@ -4806,23 +4492,11 @@ pub fn print_help() { "-C relocation-model=".color("38,139,235"), "relocation model for compatibility checks" ); - println!( - " {:<24} {}", - "-C no-default-libs".color("38,139,235"), - "Disable automatic -lc -lm" - ); + println!(" {:<24} {}", "-C no-default-libs".color("38,139,235"), "Disable automatic -lc -lm"); println!("\nPrint items:"); - println!( - " {:<24} {}", - "target-spec".color("38,139,235"), - "Target metadata for build tools" - ); - println!( - " {:<24} {}", - "supported-targets".color("38,139,235"), - "Supported target triples" - ); + println!(" {:<24} {}", "target-spec".color("38,139,235"), "Target metadata for build tools"); + println!(" {:<24} {}", "supported-targets".color("38,139,235"), "Supported target triples"); println!( " {:<24} {}", "supported-input-types".color("38,139,235"), @@ -4840,10 +4514,7 @@ pub fn print_help() { ); } -#[cfg(all( - test, - any(feature = "llvm-target-riscv", feature = "llvm-target-loongarch") -))] +#[cfg(all(test, any(feature = "llvm-target-riscv", feature = "llvm-target-loongarch")))] mod tests { use super::*; use std::sync::atomic::{AtomicU64, Ordering}; @@ -4852,12 +4523,8 @@ mod tests { fn temp_sysroot(name: &str) -> PathBuf { let sequence = NEXT_SYSROOT_CASE.fetch_add(1, Ordering::Relaxed); - let root = env::temp_dir().join(format!( - "wavec-sysroot-{}-{}-{}", - name, - process::id(), - sequence - )); + let root = + env::temp_dir().join(format!("wavec-sysroot-{}-{}-{}", name, process::id(), sequence)); let _ = fs::remove_dir_all(&root); fs::create_dir_all(root.join("lib")).unwrap(); root @@ -4889,14 +4556,8 @@ mod tests { fn std_commands_accept_only_one_explicit_reference() { let args = |items: &[&str]| items.iter().map(|s| s.to_string()).collect::>(); assert_eq!(parse_std_reference(&args(&["std"])).unwrap(), None); - for input in [ - vec!["std", "--ref", "release"], - vec!["std", "--ref=release"], - ] { - assert_eq!( - parse_std_reference(&args(&input)).unwrap().as_deref(), - Some("release") - ); + for input in [vec!["std", "--ref", "release"], vec!["std", "--ref=release"]] { + assert_eq!(parse_std_reference(&args(&input)).unwrap().as_deref(), Some("release")); } for input in [ vec![], @@ -4929,10 +4590,7 @@ mod tests { ) .unwrap(); - assert_eq!( - Path::new(&selected.path), - fs::canonicalize(&complete).unwrap() - ); + assert_eq!(Path::new(&selected.path), fs::canonicalize(&complete).unwrap()); assert_eq!(selected.source, "cross-gcc"); let _ = fs::remove_dir_all(incomplete); let _ = fs::remove_dir_all(foreign); @@ -4976,10 +4634,7 @@ mod tests { ], ) .unwrap(); - assert_eq!( - Path::new(&selected.path), - fs::canonicalize(&complete).unwrap() - ); + assert_eq!(Path::new(&selected.path), fs::canonicalize(&complete).unwrap()); assert_eq!(selected.source, "cross-gcc"); let selected_soft = select_loongarch64_linux_sysroot( diff --git a/src/errors.rs b/src/errors.rs index a30bb64a..c144feb6 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -50,7 +50,7 @@ impl CliError { } else { "command-failed" } - } + }, CliError::StdAlreadyInstalled { .. } => "std-already-installed", CliError::ExternalToolMissing(_) => "external-tool-missing", CliError::CommandFailed(_) => "command-failed", @@ -65,7 +65,7 @@ impl CliError { CliError::Backend(error) => error.to_string(), CliError::StdAlreadyInstalled { path } => { format!("std already installed at '{}'", path.display()) - } + }, CliError::ExternalToolMissing(t) => format!("required tool not found: {}", t), CliError::CommandFailed(msg) => format!("command failed: {}", msg), CliError::HomeNotSet => utils::paths::missing_home_message().to_string(), @@ -83,12 +83,8 @@ impl CliError { fields.push(("phase", Value::string(error.phase.to_string()))); fields.push(("operation", Value::string(&error.operation))); } - wson::dumps( - &Value::object([("error", Value::object(fields))]), - wson::Format::Json, - false, - ) - .expect("CLI diagnostic schema contains only shallow JSON values") + wson::dumps(&Value::object([("error", Value::object(fields))]), wson::Format::Json, false) + .expect("CLI diagnostic schema contains only shallow JSON values") } pub fn exit_code(&self) -> i32 { @@ -100,7 +96,7 @@ impl CliError { } else { 1 } - } + }, CliError::ExternalToolMissing(_) | CliError::HomeNotSet | CliError::Io(_) => 3, CliError::StdAlreadyInstalled { .. } | CliError::CommandFailed(_) => 1, } @@ -114,7 +110,7 @@ impl fmt::Display for CliError { CliError::Backend(error) => write!(f, "Error: {error}"), CliError::StdAlreadyInstalled { path } => { write!(f, "Error: std already installed at '{}'", path.display()) - } + }, CliError::ExternalToolMissing(t) => write!(f, "Error: required tool not found: {}", t), CliError::CommandFailed(msg) => write!(f, "Error: command failed: {}", msg), CliError::HomeNotSet => write!(f, "Error: {}", utils::paths::missing_home_message()), diff --git a/src/flags.rs b/src/flags.rs index cf69287a..a5f60859 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -47,8 +47,8 @@ impl DebugFlags { self.ir = true; self.mc = true; self.hex = true; - } - _ => {} + }, + _ => {}, } } } @@ -97,8 +97,5 @@ pub struct WhaleFlags { } pub fn validate_opt_flag(flag: &str) -> bool { - matches!( - flag, - "-O0" | "-O1" | "-O2" | "-O3" | "-Os" | "-Oz" | "-Ofast" - ) + matches!(flag, "-O0" | "-O1" | "-O2" | "-O3" | "-Os" | "-Oz" | "-Ofast") } diff --git a/src/link_outputs.rs b/src/link_outputs.rs index 7148f152..9e078393 100644 --- a/src/link_outputs.rs +++ b/src/link_outputs.rs @@ -36,11 +36,8 @@ pub(crate) struct MsvcOutputs { impl MsvcOutputs { pub(crate) fn prepare(output: &Path, args: &mut Vec) -> io::Result { - let mut set = Self { - outputs: Vec::new(), - directories: Vec::new(), - preserve_backups: false, - }; + let mut set = + Self { outputs: Vec::new(), directories: Vec::new(), preserve_backups: false }; let image = set.stage(output)?; for arg in args.iter_mut() { if option_value(arg, "OUT").is_some() { @@ -53,15 +50,9 @@ impl MsvcOutputs { set.rewrite_path(args, "IMPLIB", &library)?; set.stage(&library.with_extension("exp"))?; let pdb = option_path(args, "PDB").unwrap_or_else(|| output.with_extension("pdb")); - if !pdb - .to_str() - .is_some_and(|name| name.eq_ignore_ascii_case("NONE")) - { + if !pdb.to_str().is_some_and(|name| name.eq_ignore_ascii_case("NONE")) { set.rewrite_path(args, "PDB", &pdb)?; - if !args - .iter() - .any(|arg| option_value(arg, "PDBALTPATH").is_some()) - { + if !args.iter().any(|arg| option_value(arg, "PDBALTPATH").is_some()) { let final_pdb = set.outputs.last().unwrap().destination.to_string_lossy(); args.push(format!("/PDBALTPATH:{final_pdb}")); } @@ -69,10 +60,7 @@ impl MsvcOutputs { for (option, default) in [ ("ILK", output.with_extension("ilk")), ("MAP", output.with_extension("map")), - ( - "MANIFESTFILE", - PathBuf::from(format!("{}.manifest", output.display())), - ), + ("MANIFESTFILE", PathBuf::from(format!("{}.manifest", output.display()))), ] { if args.iter().any(|arg| { option_value(arg, option).is_some() @@ -106,24 +94,15 @@ impl MsvcOutputs { let filename = destination.file_name().ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidInput, "link output must name a file") })?; - let parent = destination - .parent() - .filter(|p| !p.as_os_str().is_empty()) - .unwrap_or(Path::new(".")); + let parent = + destination.parent().filter(|p| !p.as_os_str().is_empty()).unwrap_or(Path::new(".")); fs::create_dir_all(parent)?; let parent = fs::canonicalize(parent)?; let destination = parent.join(filename); - if self - .outputs - .iter() - .any(|out| out.destination == destination) - { + if self.outputs.iter().any(|out| out.destination == destination) { return Err(io::Error::new( io::ErrorKind::InvalidInput, - format!( - "link outputs share the same destination: {}", - destination.display() - ), + format!("link outputs share the same destination: {}", destination.display()), )); } let directory = @@ -142,16 +121,13 @@ impl MsvcOutputs { Ok(()) => { selected = Some(path); break; - } + }, Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue, Err(e) => return Err(e), } } let staged = selected.ok_or_else(|| { - io::Error::new( - io::ErrorKind::AlreadyExists, - "link staging directory collision", - ) + io::Error::new(io::ErrorKind::AlreadyExists, "link staging directory collision") })?; self.directories.push((parent, staged.clone())); staged @@ -190,10 +166,7 @@ impl MsvcOutputs { if output.destination.exists() && !output.destination.is_file() { return Err(io::Error::new( io::ErrorKind::InvalidInput, - format!( - "link destination is not a file: {}", - output.destination.display() - ), + format!("link destination is not a file: {}", output.destination.display()), )); } } @@ -241,17 +214,12 @@ impl MsvcOutputs { } fn option_value<'a>(arg: &'a str, option: &str) -> Option<&'a str> { - let (name, value) = arg - .strip_prefix('/') - .or_else(|| arg.strip_prefix('-'))? - .split_once(':')?; + let (name, value) = arg.strip_prefix('/').or_else(|| arg.strip_prefix('-'))?.split_once(':')?; name.eq_ignore_ascii_case(option).then_some(value) } fn option_path(args: &[String], option: &str) -> Option { - args.iter() - .rev() - .find_map(|arg| option_value(arg, option).map(PathBuf::from)) + args.iter().rev().find_map(|arg| option_value(arg, option).map(PathBuf::from)) } impl Drop for MsvcOutputs { @@ -305,10 +273,8 @@ mod tests { let root = directory("alias"); let dll = root.join("answer.dll"); fs::write(&dll, b"old image").unwrap(); - let mut args = vec![ - format!("/OUT:{}", dll.display()), - format!("/IMPLIB:{}", dll.display()), - ]; + let mut args = + vec![format!("/OUT:{}", dll.display()), format!("/IMPLIB:{}", dll.display())]; assert!(MsvcOutputs::prepare(&dll, &mut args).is_err()); assert_eq!(fs::read(&dll).unwrap(), b"old image"); assert_eq!(fs::read_dir(&root).unwrap().count(), 1); diff --git a/src/link_validation/elf.rs b/src/link_validation/elf.rs index 611d1d92..a3bd2797 100644 --- a/src/link_validation/elf.rs +++ b/src/link_validation/elf.rs @@ -33,14 +33,8 @@ pub(super) struct ElfMetadata { #[derive(Debug)] pub enum LinkInputInspectionError { - Read { - input: PathBuf, - source: std::io::Error, - }, - Malformed { - input: String, - reason: String, - }, + Read { input: PathBuf, source: std::io::Error }, + Malformed { input: String, reason: String }, } impl fmt::Display for LinkInputInspectionError { @@ -53,12 +47,8 @@ impl fmt::Display for LinkInputInspectionError { source ), Self::Malformed { input, reason } => { - write!( - formatter, - "invalid ELF linker input '{}': {}", - input, reason - ) - } + write!(formatter, "invalid ELF linker input '{}': {}", input, reason) + }, } } } @@ -218,10 +208,7 @@ fn inspect_archive( } } if selected.is_some() { - return Err(malformed( - &display, - "nested archive member offset is not a member header", - )); + return Err(malformed(&display, "nested archive member offset is not a member header")); } Ok(()) } @@ -231,9 +218,7 @@ fn archive_number( text: &[u8], reason: &str, ) -> Result { - let text = std::str::from_utf8(text) - .map_err(|_| malformed(display, reason))? - .trim(); + let text = std::str::from_utf8(text).map_err(|_| malformed(display, reason))?.trim(); if text.is_empty() || !text.bytes().all(|c| c.is_ascii_digit()) { return Err(malformed(display, reason)); } @@ -249,23 +234,14 @@ fn archive_member<'a>( ) -> Result<(String, &'a [u8], Option), LinkInputInspectionError> { if let Some(length) = raw_name.strip_prefix("#1/") { if thin { - return Err(malformed( - display, - "BSD extended names are invalid in GNU thin archives", - )); + return Err(malformed(display, "BSD extended names are invalid in GNU thin archives")); } - let length = archive_number( - display, - length.as_bytes(), - "invalid BSD archive member name", - )?; + let length = archive_number(display, length.as_bytes(), "invalid BSD archive member name")?; let name = data .get(..length) .ok_or_else(|| malformed(display, "truncated BSD archive member name"))?; return Ok(( - String::from_utf8_lossy(name) - .trim_end_matches('\0') - .to_string(), + String::from_utf8_lossy(name).trim_end_matches('\0').to_string(), &data[length..], None, )); @@ -282,11 +258,8 @@ fn archive_member<'a>( ), _ => (reference, None), }; - let offset = archive_number( - display, - name_offset.as_bytes(), - "invalid GNU archive name offset", - )?; + let offset = + archive_number(display, name_offset.as_bytes(), "invalid GNU archive name offset")?; let table = long_names.ok_or_else(|| malformed(display, "archive long-name table is missing"))?; let tail = table @@ -310,10 +283,7 @@ fn archive_member<'a>( } fn malformed(input: &str, reason: &str) -> LinkInputInspectionError { - LinkInputInspectionError::Malformed { - input: input.to_string(), - reason: reason.to_string(), - } + LinkInputInspectionError::Malformed { input: input.to_string(), reason: reason.to_string() } } fn read_u16(bytes: &[u8], little_endian: bool) -> u16 { @@ -381,6 +351,7 @@ mod tests { fs::create_dir_all(path.join("objects")).unwrap(); Self(path) } + fn inspect(&self, bytes: &[u8]) -> Result, LinkInputInspectionError> { let path = self.0.join("lib.a"); fs::write(&path, bytes).unwrap(); @@ -402,6 +373,7 @@ mod tests { bytes[48..52].copy_from_slice(&flags.to_le_bytes()); bytes } + fn thin(names: &[u8], members: &[(&str, usize)]) -> Vec { let mut bytes = THIN_MAGIC.to_vec(); bytes.extend(archive_header("/", 4)); @@ -433,10 +405,7 @@ mod tests { ) .unwrap_err() .to_string(); - assert!( - error.contains("other.o") && error.contains("LP64F"), - "{error}" - ); + assert!(error.contains("other.o") && error.contains("LP64F"), "{error}"); } #[test] @@ -465,22 +434,14 @@ mod tests { assert_eq!(metadata.len(), 1); assert_eq!(metadata[0].flags, 4); let bad = thin(b"nested.a/\n", &[("/0:9", 64)]); - assert!(fixture - .inspect(&bad) - .unwrap_err() - .to_string() - .contains("not a member header")); + assert!(fixture.inspect(&bad).unwrap_err().to_string().contains("not a member header")); } #[test] fn thin_missing_members_and_malformed_headers_are_diagnostics() { let fixture = Fixture::new(); let valid = thin(b"missing.o/\n", &[("/0", 64)]); - assert!(fixture - .inspect(&valid) - .unwrap_err() - .to_string() - .contains("missing.o")); + assert!(fixture.inspect(&valid).unwrap_err().to_string().contains("missing.o")); for bytes in [ thin(b"missing.o/\n", &[("/999", 64)]), thin(b"missing.o/\n", &[("/1", 64)]), @@ -492,10 +453,6 @@ mod tests { assert!(fixture.inspect(&bytes).is_err()); } let cycle = thin(b"lib.a/\n", &[("/0", 64)]); - assert!(fixture - .inspect(&cycle) - .unwrap_err() - .to_string() - .contains("nesting limit")); + assert!(fixture.inspect(&cycle).unwrap_err().to_string().contains("nesting limit")); } } diff --git a/src/link_validation/loongarch.rs b/src/link_validation/loongarch.rs index 0702e7d1..316f1982 100644 --- a/src/link_validation/loongarch.rs +++ b/src/link_validation/loongarch.rs @@ -58,14 +58,8 @@ impl fmt::Display for LoongArchFloatAbi { #[derive(Debug)] pub enum LoongArchAbiValidationError { Inspection(LinkInputInspectionError), - Unsupported { - input: String, - }, - Mismatch { - target: LoongArchFloatAbi, - input: String, - input_abi: LoongArchFloatAbi, - }, + Unsupported { input: String }, + Mismatch { target: LoongArchFloatAbi, input: String, input_abi: LoongArchFloatAbi }, } impl fmt::Display for LoongArchAbiValidationError { @@ -110,9 +104,7 @@ pub fn validate_loongarch64_link_inputs( continue; } let input_abi = LoongArchFloatAbi::from_elf_flags(metadata.flags).ok_or_else(|| { - LoongArchAbiValidationError::Unsupported { - input: metadata.input.clone(), - } + LoongArchAbiValidationError::Unsupported { input: metadata.input.clone() } })?; if input_abi != target_abi { return Err(LoongArchAbiValidationError::Mismatch { diff --git a/src/link_validation/riscv.rs b/src/link_validation/riscv.rs index b13f7c4e..71b81d5a 100644 --- a/src/link_validation/riscv.rs +++ b/src/link_validation/riscv.rs @@ -62,14 +62,8 @@ impl fmt::Display for RiscvFloatAbi { #[derive(Debug)] pub enum AbiValidationError { Inspection(LinkInputInspectionError), - Unsupported { - input: String, - }, - Mismatch { - target: RiscvFloatAbi, - input: String, - input_abi: RiscvFloatAbi, - }, + Unsupported { input: String }, + Mismatch { target: RiscvFloatAbi, input: String, input_abi: RiscvFloatAbi }, } impl fmt::Display for AbiValidationError { @@ -113,11 +107,8 @@ pub fn validate_riscv_link_inputs( if metadata.machine != EM_RISCV { continue; } - let input_abi = RiscvFloatAbi::from_elf_flags(metadata.flags).ok_or_else(|| { - AbiValidationError::Unsupported { - input: metadata.input.clone(), - } - })?; + let input_abi = RiscvFloatAbi::from_elf_flags(metadata.flags) + .ok_or_else(|| AbiValidationError::Unsupported { input: metadata.input.clone() })?; if input_abi != target_abi { return Err(AbiValidationError::Mismatch { target: target_abi, diff --git a/src/main.rs b/src/main.rs index 87ebc349..5a739cfe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,9 +20,7 @@ use std::process; fn main() { let json_errors = wavec::cli::args_request_json_errors( - std::env::args_os() - .skip(1) - .map(|arg| arg.to_string_lossy().into_owned()), + std::env::args_os().skip(1).map(|arg| arg.to_string_lossy().into_owned()), ); if let Err(e) = wavec::cli::run() { diff --git a/src/module_resolver.rs b/src/module_resolver.rs index f091e443..e9014ea0 100644 --- a/src/module_resolver.rs +++ b/src/module_resolver.rs @@ -98,9 +98,7 @@ pub fn demangle_module_names(text: &str) -> String { out.push_str(&rest[..index]); let candidate = &rest[index + PREFIX.len()..]; if candidate.len() >= 17 - && candidate.as_bytes()[..16] - .iter() - .all(|byte| byte.is_ascii_hexdigit()) + && candidate.as_bytes()[..16].iter().all(|byte| byte.is_ascii_hexdigit()) && candidate.as_bytes()[16] == b'_' { rest = &candidate[17..]; @@ -140,10 +138,7 @@ impl Resolver<'_> { self.visiting.push(key.clone()); let mut interface = collect_symbols(&key, &ast, is_entry)?; - let mut names = NameContext { - own: interface.symbols.clone(), - ..NameContext::default() - }; + let mut names = NameContext { own: interface.symbols.clone(), ..NameContext::default() }; for node in &ast { let ASTNode::Statement(StatementNode::Import(import)) = node.unspanned() else { @@ -159,10 +154,8 @@ impl Resolver<'_> { *index } else { let index = self.sources.len(); - self.sources.push(ModuleSource { - path: unit.abs_path.clone(), - source: unit.source, - }); + self.sources + .push(ModuleSource { path: unit.abs_path.clone(), source: unit.source }); self.source_indices.insert(child_key.clone(), index); index }; @@ -188,9 +181,7 @@ impl Resolver<'_> { if qualified.starts_with(&prefix) && constructor.kind == SymbolKind::VariantConstructor { - interface - .symbols - .insert(qualified.clone(), constructor.clone()); + interface.symbols.insert(qualified.clone(), constructor.clone()); } } } @@ -199,10 +190,7 @@ impl Resolver<'_> { let mut lowered = Vec::new(); for node in ast { - if matches!( - node.unspanned(), - ASTNode::Statement(StatementNode::Import(_)) - ) { + if matches!(node.unspanned(), ASTNode::Statement(StatementNode::Import(_))) { continue; } lowered.push(rewrite_top_level(node, &names, &key, is_entry)?); @@ -210,8 +198,7 @@ impl Resolver<'_> { self.visiting.pop(); self.interfaces.insert(key, interface.clone()); - self.origins - .extend(std::iter::repeat_n(origin, lowered.len())); + self.origins.extend(std::iter::repeat_n(origin, lowered.len())); self.ast.extend(lowered); Ok(interface) } @@ -267,11 +254,7 @@ fn insert_symbol( } symbols.insert( source_name.to_string(), - ModuleSymbol { - lowered: internal_name(path, source_name, is_entry), - visibility, - kind, - }, + ModuleSymbol { lowered: internal_name(path, source_name, is_entry), visibility, kind }, ); Ok(()) } @@ -301,7 +284,7 @@ fn collect_symbols( SymbolKind::Function, is_entry, )?; - } + }, ASTNode::ExternFunction(function) => insert_symbol( path, &mut symbols, @@ -353,7 +336,7 @@ fn collect_symbols( }, ); } - } + }, ASTNode::Variant(variant) => { insert_symbol( path, @@ -374,7 +357,7 @@ fn collect_symbols( }, ); } - } + }, ASTNode::Variable(variable) if matches!(variable.mutability, Mutability::Const | Mutability::Static) => { @@ -386,8 +369,8 @@ fn collect_symbols( SymbolKind::Value, is_entry, )?; - } - _ => {} + }, + _ => {}, } } Ok(ModuleInterface { symbols }) @@ -395,10 +378,7 @@ fn collect_symbols( fn default_namespace(path: &str) -> Option { if path.starts_with("./") { - return Path::new(path) - .file_stem() - .and_then(|name| name.to_str()) - .map(str::to_string); + return Path::new(path).file_stem().and_then(|name| name.to_str()).map(str::to_string); } Some(path.to_string()) } @@ -423,10 +403,7 @@ fn bind_import( return Err(module_error( current_path, "Private imported symbol", - format!( - "symbol '{}' is private in module '{}'", - selected, import.path - ), + format!("symbol '{}' is private in module '{}'", selected, import.path), "mark the declaration `pub` or remove it from the import list", )); } @@ -455,20 +432,15 @@ fn bind_import( if qualified.starts_with(&prefix) && constructor.kind == SymbolKind::VariantConstructor { - names - .selected - .insert(qualified.clone(), constructor.clone()); + names.selected.insert(qualified.clone(), constructor.clone()); } } } return Ok(()); } - let namespace = import - .alias - .clone() - .or_else(|| default_namespace(&import.path)) - .ok_or_else(|| { + let namespace = + import.alias.clone().or_else(|| default_namespace(&import.path)).ok_or_else(|| { module_error( current_path, "Invalid import namespace", @@ -503,9 +475,7 @@ fn resolve_name( } let mut best: Option<(&str, &ModuleInterface)> = None; for (namespace, interface) in &names.namespaces { - if name - .strip_prefix(namespace) - .is_some_and(|rest| rest.starts_with("::")) + if name.strip_prefix(namespace).is_some_and(|rest| rest.starts_with("::")) && best.is_none_or(|(current, _)| namespace.len() > current.len()) { best = Some((namespace, interface)); @@ -532,35 +502,27 @@ fn resolve_name( return Err(module_error( path, "Private imported symbol", - format!( - "symbol '{}' is private in module '{}'", - symbol_name, namespace - ), + format!("symbol '{}' is private in module '{}'", symbol_name, namespace), "only `pub` declarations are accessible outside their module", )); } return Ok(Some(symbol.clone())); } - Ok(names - .own - .get(name) - .or_else(|| names.selected.get(name)) - .cloned()) + Ok(names.own.get(name).or_else(|| names.selected.get(name)).cloned()) } fn rewrite_type(ty: WaveType, names: &NameContext, path: &Path) -> Result { match ty { - WaveType::Future(inner) => Ok(WaveType::Future(Box::new(rewrite_type( - *inner, names, path, - )?))), - WaveType::Pointer(inner) => Ok(WaveType::Pointer(Box::new(rewrite_type( - *inner, names, path, - )?))), - WaveType::Array(inner, size) => Ok(WaveType::Array( - Box::new(rewrite_type(*inner, names, path)?), - size, - )), + WaveType::Future(inner) => { + Ok(WaveType::Future(Box::new(rewrite_type(*inner, names, path)?))) + }, + WaveType::Pointer(inner) => { + Ok(WaveType::Pointer(Box::new(rewrite_type(*inner, names, path)?))) + }, + WaveType::Array(inner, size) => { + Ok(WaveType::Array(Box::new(rewrite_type(*inner, names, path)?), size)) + }, WaveType::Struct(name) => Ok(WaveType::Struct(rewrite_type_name(&name, names, path)?)), WaveType::Variant(name) => Ok(WaveType::Variant(rewrite_type_name(&name, names, path)?)), other => Ok(other), @@ -574,17 +536,13 @@ fn rewrite_type_name(name: &str, names: &NameContext, path: &Path) -> Result, _>>()?; - return Ok(format!( - "{}<{}>", - rewritten_base, - rewritten_arguments.join(",") - )); + return Ok(format!("{}<{}>", rewritten_base, rewritten_arguments.join(","))); } match resolve_name(name, names, path)? { Some(symbol) if matches!(symbol.kind, SymbolKind::Struct | SymbolKind::Type) => { Ok(symbol.lowered) - } + }, Some(_) => Err(module_error( path, "Expected imported type", @@ -614,8 +572,8 @@ fn split_type_arguments(arguments: &str) -> Vec<&str> { ',' if depth == 0 => { out.push(&arguments[start..index]); start = index + 1; - } - _ => {} + }, + _ => {}, } } out.push(&arguments[start..]); @@ -631,7 +589,7 @@ fn rewrite_top_level( match node { ASTNode::Located { value, span } => { Ok(rewrite_top_level(*value, names, path, is_entry)?.with_span(Some(span))) - } + }, ASTNode::Function(mut function) => { let original = function.name.clone(); function.name = names.own[&original].lowered.clone(); @@ -647,7 +605,7 @@ fn rewrite_top_level( } rewrite_function(&mut function, names, path)?; Ok(ASTNode::Function(function)) - } + }, ASTNode::ExternFunction(mut function) => { function.name = names.own[&function.name].lowered.clone(); function.params = function @@ -657,7 +615,7 @@ fn rewrite_top_level( .collect::>()?; function.return_type = rewrite_type(function.return_type, names, path)?; Ok(ASTNode::ExternFunction(function)) - } + }, ASTNode::Struct(mut structure) => { structure.name = names.own[&structure.name].lowered.clone(); structure.fields = structure @@ -669,12 +627,12 @@ fn rewrite_top_level( rewrite_function(method, names, path)?; } Ok(ASTNode::Struct(structure)) - } + }, ASTNode::TypeAlias(mut alias) => { alias.name = names.own[&alias.name].lowered.clone(); alias.target = rewrite_type(alias.target, names, path)?; Ok(ASTNode::TypeAlias(alias)) - } + }, ASTNode::Enum(mut enumeration) => { enumeration.name = names.own[&enumeration.name].lowered.clone(); enumeration.repr_type = rewrite_type(enumeration.repr_type, names, path)?; @@ -682,7 +640,7 @@ fn rewrite_top_level( variant.name = names.own[&variant.name].lowered.clone(); } Ok(ASTNode::Enum(enumeration)) - } + }, ASTNode::Variant(mut variant) => { variant.name = names.own[&variant.name].lowered.clone(); for case in &mut variant.cases { @@ -693,7 +651,7 @@ fn rewrite_top_level( .collect::>()?; } Ok(ASTNode::Variant(variant)) - } + }, ASTNode::Variable(mut variable) => { variable.name = names.own[&variable.name].lowered.clone(); variable.type_name = rewrite_type(variable.type_name, names, path)?; @@ -702,7 +660,7 @@ fn rewrite_top_level( Some(rewrite_expression(value, names, path, &HashSet::new())?); } Ok(ASTNode::Variable(variable)) - } + }, ASTNode::ProtoImpl(mut implementation) => { implementation.target = resolve_name(&implementation.target, names, path)? .map_or(implementation.target, |symbol| symbol.lowered); @@ -710,19 +668,13 @@ fn rewrite_top_level( rewrite_function(method, names, path)?; } Ok(ASTNode::ProtoImpl(implementation)) - } - ASTNode::Statement(statement) => Ok(ASTNode::Statement(rewrite_statement( - statement, - names, - path, - &mut HashSet::new(), - )?)), - ASTNode::Expression(expression) => Ok(ASTNode::Expression(rewrite_expression( - expression, - names, - path, - &HashSet::new(), - )?)), + }, + ASTNode::Statement(statement) => { + Ok(ASTNode::Statement(rewrite_statement(statement, names, path, &mut HashSet::new())?)) + }, + ASTNode::Expression(expression) => { + Ok(ASTNode::Expression(rewrite_expression(expression, names, path, &HashSet::new())?)) + }, other => Ok(other), } } @@ -735,16 +687,10 @@ fn rewrite_function( for parameter in &mut function.parameters { parameter.param_type = rewrite_type(parameter.param_type.clone(), names, path)?; } - function.return_type = function - .return_type - .take() - .map(|ty| rewrite_type(ty, names, path)) - .transpose()?; - let mut locals = function - .parameters - .iter() - .map(|parameter| parameter.name.clone()) - .collect::>(); + function.return_type = + function.return_type.take().map(|ty| rewrite_type(ty, names, path)).transpose()?; + let mut locals = + function.parameters.iter().map(|parameter| parameter.name.clone()).collect::>(); function.body = rewrite_block(std::mem::take(&mut function.body), names, path, &mut locals)?; Ok(()) } @@ -760,12 +706,8 @@ fn rewrite_block( match node { ASTNode::Located { value, span } => { let rewritten = rewrite_block(vec![*value], names, path, locals)?; - out.extend( - rewritten - .into_iter() - .map(|node| node.with_span(Some(span.clone()))), - ); - } + out.extend(rewritten.into_iter().map(|node| node.with_span(Some(span.clone())))); + }, ASTNode::Variable(mut variable) => { variable.type_name = rewrite_type(variable.type_name, names, path)?; if let Some(value) = variable.initial_value.take() { @@ -773,13 +715,13 @@ fn rewrite_block( } locals.insert(variable.name.clone()); out.push(ASTNode::Variable(variable)); - } - ASTNode::Statement(statement) => out.push(ASTNode::Statement(rewrite_statement( - statement, names, path, locals, - )?)), - ASTNode::Expression(expression) => out.push(ASTNode::Expression(rewrite_expression( - expression, names, path, locals, - )?)), + }, + ASTNode::Statement(statement) => { + out.push(ASTNode::Statement(rewrite_statement(statement, names, path, locals)?)) + }, + ASTNode::Expression(expression) => { + out.push(ASTNode::Expression(rewrite_expression(expression, names, path, locals)?)) + }, other => out.push(other), } } @@ -801,16 +743,10 @@ fn rewrite_statement( format, args: rewrite_expressions(args, names, path, locals)?, }, - StatementNode::Input { format, args } => StatementNode::Input { - format, - args: rewrite_expressions(args, names, path, locals)?, + StatementNode::Input { format, args } => { + StatementNode::Input { format, args: rewrite_expressions(args, names, path, locals)? } }, - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } => { + StatementNode::If { condition, body, else_if_blocks, else_block } => { let condition = rewrite_expression(condition, names, path, locals)?; let mut body_scope = locals.clone(); let body = rewrite_block(body, names, path, &mut body_scope)?; @@ -833,19 +769,9 @@ fn rewrite_statement( rewrite_block(*body, names, path, &mut scope).map(Box::new) }) .transpose()?; - StatementNode::If { - condition, - body, - else_if_blocks, - else_block, - } - } - StatementNode::For { - initialization, - condition, - increment, - body, - } => { + StatementNode::If { condition, body, else_if_blocks, else_block } + }, + StatementNode::For { initialization, condition, increment, body } => { let mut scope = locals.clone(); let initialization = rewrite_block(vec![*initialization], names, path, &mut scope)? .into_iter() @@ -860,13 +786,13 @@ fn rewrite_statement( increment, body, } - } + }, StatementNode::While { condition, body } => { let condition = rewrite_expression(condition, names, path, locals)?; let mut scope = locals.clone(); let body = rewrite_block(body, names, path, &mut scope)?; StatementNode::While { condition, body } - } + }, StatementNode::Match { value, arms } => StatementNode::Match { value: rewrite_expression(value, names, path, locals)?, arms: arms @@ -888,32 +814,23 @@ fn rewrite_statement( }, value: rewrite_expression(value, names, path, locals)?, }, - StatementNode::AsmBlock { - instructions, - inputs, - outputs, - clobbers, - } => StatementNode::AsmBlock { - instructions, - inputs: inputs - .into_iter() - .map(|(constraint, expression)| { - Ok(( - constraint, - rewrite_expression(expression, names, path, locals)?, - )) - }) - .collect::>()?, - outputs: outputs - .into_iter() - .map(|(constraint, expression)| { - Ok(( - constraint, - rewrite_expression(expression, names, path, locals)?, - )) - }) - .collect::>()?, - clobbers, + StatementNode::AsmBlock { instructions, inputs, outputs, clobbers } => { + StatementNode::AsmBlock { + instructions, + inputs: inputs + .into_iter() + .map(|(constraint, expression)| { + Ok((constraint, rewrite_expression(expression, names, path, locals)?)) + }) + .collect::>()?, + outputs: outputs + .into_iter() + .map(|(constraint, expression)| { + Ok((constraint, rewrite_expression(expression, names, path, locals)?)) + }) + .collect::>()?, + clobbers, + } }, StatementNode::Return(value) => StatementNode::Return( value @@ -922,7 +839,7 @@ fn rewrite_statement( ), StatementNode::Expression(expression) => { StatementNode::Expression(rewrite_expression(expression, names, path, locals)?) - } + }, other => other, }) } @@ -954,12 +871,8 @@ fn rewrite_match_pattern( *name = symbol.lowered; } } - } - MatchPattern::Variant { - variant_type, - case_name, - payloads, - } => { + }, + MatchPattern::Variant { variant_type, case_name, payloads } => { let qualified = format!("{}::{}", variant_type, case_name); let symbol = resolve_name(&qualified, names, path)?.ok_or_else(|| { module_error( @@ -983,8 +896,8 @@ fn rewrite_match_pattern( for payload in payloads { rewrite_match_pattern(payload, names, path, locals)?; } - } - MatchPattern::Int(_) | MatchPattern::Binding(_) | MatchPattern::Wildcard => {} + }, + MatchPattern::Int(_) | MatchPattern::Binding(_) | MatchPattern::Wildcard => {}, } Ok(()) } @@ -994,13 +907,13 @@ fn collect_pattern_bindings(pattern: &MatchPattern, locals: &mut HashSet MatchPattern::Located { value, .. } => collect_pattern_bindings(value, locals), MatchPattern::Binding(name) => { locals.insert(name.clone()); - } + }, MatchPattern::Variant { payloads, .. } => { for payload in payloads { collect_pattern_bindings(payload, locals); } - } - MatchPattern::Int(_) | MatchPattern::Ident(_) | MatchPattern::Wildcard => {} + }, + MatchPattern::Int(_) | MatchPattern::Ident(_) | MatchPattern::Wildcard => {}, } } @@ -1037,16 +950,11 @@ fn rewrite_expression_in_place( *ty = rewrite_type(std::mem::replace(ty, WaveType::Void), names, path)?; } return Ok(()); - } + }, Work::Failure(error) => return Err(*error), Work::Expression(expression) => expression, }; - if let Expression::FunctionCall { - name, - type_args, - args, - } = expression - { + if let Expression::FunctionCall { name, type_args, args } = expression { if type_args.is_empty() && args.is_empty() { if let Some(symbol) = resolve_name(name, names, path)? { if symbol.kind == SymbolKind::Struct { @@ -1064,18 +972,14 @@ fn rewrite_expression_in_place( // Recursive callers previously applied the outermost span // last. Retain that diagnostic location without recursion. pending.push((Work::Expression(value), enclosing_span.or(Some(&*span)))); - } + }, Expression::StructLiteral { name, fields } => { *name = rewrite_type_name(name, names, path)?; for (_, value) in fields.iter_mut().rev() { pending.push((Work::Expression(value), enclosing_span)); } - } - Expression::FunctionCall { - name, - type_args, - args, - } => { + }, + Expression::FunctionCall { name, type_args, args } => { let symbol = resolve_name(name, names, path)?; for ty in type_args.iter_mut() { *ty = rewrite_type(std::mem::replace(ty, WaveType::Void), names, path)?; @@ -1090,7 +994,7 @@ fn rewrite_expression_in_place( format!("struct '{}' must be initialized with named fields", name), "use `Type { field: value }`; `Type()` is only valid for empty structs", ))), enclosing_span)); - } + }, Some(symbol) if matches!( symbol.kind, @@ -1098,7 +1002,7 @@ fn rewrite_expression_in_place( ) => { *name = symbol.lowered; - } + }, Some(_) => { pending.push(( Work::Failure(Box::new(module_error( @@ -1109,82 +1013,72 @@ fn rewrite_expression_in_place( ))), enclosing_span, )); - } - None => {} + }, + None => {}, } // Arguments still report errors before a non-callable // symbol/invalid-constructor error, in source order. for arg in args.iter_mut().rev() { pending.push((Work::Expression(arg), enclosing_span)); } - } - Expression::MethodCall { - object, - type_args, - args, - .. - } => { + }, + Expression::MethodCall { object, type_args, args, .. } => { for arg in args.iter_mut().rev() { pending.push((Work::Expression(arg), enclosing_span)); } pending.push((Work::Types(type_args), enclosing_span)); pending.push((Work::Expression(object), enclosing_span)); - } + }, Expression::Variable(name) => { if !locals.contains(name) { if let Some(symbol) = resolve_name(name, names, path)? { *name = symbol.lowered; } } - } + }, Expression::Deref(inner) | Expression::AddressOf(inner) | Expression::Await(inner) | Expression::Grouped(inner) => { pending.push((Work::Expression(inner), enclosing_span)) - } + }, Expression::BinaryExpression { left, right, .. } => { pending.push((Work::Expression(right), enclosing_span)); pending.push((Work::Expression(left), enclosing_span)); - } + }, Expression::IndexAccess { target, index } => { pending.push((Work::Expression(index), enclosing_span)); pending.push((Work::Expression(target), enclosing_span)); - } + }, Expression::ArrayLiteral(values) => { for value in values.iter_mut().rev() { pending.push((Work::Expression(value), enclosing_span)); } - } + }, Expression::AssignOperation { target, value, .. } | Expression::Assignment { target, value } => { pending.push((Work::Expression(value), enclosing_span)); pending.push((Work::Expression(target), enclosing_span)); - } - Expression::AsmBlock { - inputs, outputs, .. - } => { + }, + Expression::AsmBlock { inputs, outputs, .. } => { for (_, value) in inputs.iter_mut().chain(outputs).rev() { pending.push((Work::Expression(value), enclosing_span)); } - } + }, Expression::FieldAccess { object, .. } => { pending.push((Work::Expression(object), enclosing_span)); - } + }, Expression::Unary { expr, .. } => { pending.push((Work::Expression(expr), enclosing_span)) - } + }, Expression::Cast { expr, target_type } => { - pending.push(( - Work::Types(std::slice::from_mut(target_type)), - enclosing_span, - )); + pending.push((Work::Types(std::slice::from_mut(target_type)), enclosing_span)); pending.push((Work::Expression(expr), enclosing_span)); - } + }, Expression::IncDec { target, .. } => { pending.push((Work::Expression(target), enclosing_span)) - } - Expression::Null | Expression::Literal(_) => {} + }, + Expression::Null | Expression::Literal(_) => {}, } Ok(()) })(); diff --git a/src/runner.rs b/src/runner.rs index 76497466..ca8b25ee 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -69,6 +69,7 @@ fn target_condition_context_for_llvm(llvm: Option<&LlvmFlags>) -> TargetConditio target } + fn parse_wave_tokens_or_exit( file_path: &Path, source: &str, @@ -98,9 +99,7 @@ fn parse_wave_tokens_or_exit( .with_code(code) .with_source_code(source.to_string()); - wave_err = wave_err - .with_span(err.span()) - .with_related(err.related().iter().cloned()); + wave_err = wave_err.with_span(err.span()).with_related(err.related().iter().cloned()); if let Some(ctx) = err.context() { wave_err = wave_err.with_context(ctx.to_string()); } @@ -154,7 +153,7 @@ fn lower_wave_hir_or_exit(file_path: &Path, source: &str, ast: Vec) -> error.display_auto(); process::exit(1); - } + }, } } @@ -169,20 +168,13 @@ fn validate_expanded_ast_or_exit(expanded: &ExpandedWaveAst) { if let Some(primary) = &mut diagnostic.primary { primary.text = demangle_module_names(&primary.text); } - let origin = expanded - .origins - .get(diagnostic.top_level_index) - .copied() - .unwrap_or(0); + let origin = expanded.origins.get(diagnostic.top_level_index).copied().unwrap_or(0); let source_unit = expanded.sources.get(origin).unwrap_or(&expanded.sources[0]); let span = diagnostic.span.clone(); let diagnostic_source = span .as_ref() .and_then(|span| { - expanded - .sources - .iter() - .find(|unit| unit.path.to_string_lossy() == span.file) + expanded.sources.iter().find(|unit| unit.path.to_string_lossy() == span.file) }) .map(|unit| unit.source.clone()) .unwrap_or_else(|| source_unit.source.clone()); @@ -235,9 +227,7 @@ where "non-string panic payload".to_string() }; - let loc = info - .location() - .map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column())); + let loc = info.location().map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column())); if let Ok(mut guard) = hook_state.lock() { *guard = Some((payload, loc)); @@ -251,20 +241,13 @@ where Ok(v) => Ok(v), Err(payload) => { let fallback = panic_payload_to_string(&*payload); - let captured = captured - .lock() - .ok() - .and_then(|g| g.clone()) - .unwrap_or((fallback.clone(), None)); - - let msg = if captured.0.trim().is_empty() { - fallback - } else { - captured.0 - }; + let captured = + captured.lock().ok().and_then(|g| g.clone()).unwrap_or((fallback.clone(), None)); + + let msg = if captured.0.trim().is_empty() { fallback } else { captured.0 }; Err((msg, captured.1)) - } + }, } } @@ -280,13 +263,11 @@ fn emit_backend_error_and_exit( 0, 0, ) - .with_code( - if error.kind == llvm::diagnostic::CodegenErrorKind::InvalidAssembly { - "E3401" - } else { - "E9002" - }, - ) + .with_code(if error.kind == llvm::diagnostic::CodegenErrorKind::InvalidAssembly { + "E3401" + } else { + "E9002" + }) .with_context(format!("compiler phase: {}", error.phase)); if let Some(span) = error.span { let contents = if Path::new(&span.file) == file_path { @@ -344,9 +325,7 @@ fn build_import_config(dep: &DepFlags, target: TargetConditionContext) -> Import } for package in &dep.packages { - config - .dep_packages - .insert(package.name.clone(), PathBuf::from(&package.path)); + config.dep_packages.insert(package.name.clone(), PathBuf::from(&package.path)); } config @@ -396,11 +375,7 @@ fn expand_imports_for_codegen( import_config: &ImportConfig, ) -> Result { let mut graph = resolve_import_graph(entry_path, entry_source, ast, import_config)?; - let pointer_bits = if import_config.target.arch.as_deref() == Some("wasm32") { - 32 - } else { - 64 - }; + let pointer_bits = if import_config.target.arch.as_deref() == Some("wasm32") { 32 } else { 64 }; ::hir::resolve_target_types(&mut graph.ast, pointer_bits).map_err(|message| { WaveError::new( WaveErrorKind::InvalidStatement(message.clone()), @@ -416,10 +391,7 @@ fn expand_imports_for_codegen( sources: graph .sources .into_iter() - .map(|source| SemanticSourceUnit { - path: source.path, - source: source.source, - }) + .map(|source| SemanticSourceUnit { path: source.path, source: source.source }) .collect(), }) } @@ -462,7 +434,7 @@ fn utf8_output_path(path: &Path, file_path: &Path) -> String { .with_help("pass a UTF-8 output path with -o ") .display_auto(); process::exit(1); - } + }, } } @@ -499,11 +471,7 @@ fn resolve_output_target( if let Err(err) = fs::create_dir_all(parent) { WaveError::new( WaveErrorKind::FileWriteError(output.display().to_string()), - format!( - "failed to create output directory `{}`: {}", - parent.display(), - err - ), + format!("failed to create output directory `{}`: {}", parent.display(), err), file_path.display().to_string(), 0, 0, @@ -557,7 +525,7 @@ fn frontend_prepare_wave_hir( .with_help("check if the file exists and you have permission to read it") .display_auto(); process::exit(1); - } + }, }; let target = target_condition_context_for_llvm(llvm); let code = preprocess_target_attrs(&raw_code, &target); @@ -590,7 +558,7 @@ fn frontend_prepare_wave_hir( Err(e) => { e.display_auto(); process::exit(1); - } + }, }; // Validate both sides of monomorphization: templates must be semantically // sound, and generated concrete nodes must satisfy the same language rules. @@ -613,7 +581,7 @@ fn frontend_prepare_wave_hir( ) .display_auto(); process::exit(1); - } + }, }; let hir = lower_wave_hir_or_exit(file_path, &code, ast); @@ -648,7 +616,7 @@ pub(crate) fn emit_whale_ir_text( .with_context("Whale IR lowering") .display_auto(); process::exit(1); - } + }, } } @@ -690,7 +658,7 @@ fn print_ast_dump(ast: &[::parser::ast::ASTNode], code: &str, file: &Path) { Err(error) => { eprintln!("AST dump failed: {error}"); process::exit(1); - } + }, } } @@ -709,7 +677,7 @@ pub(crate) unsafe fn emit_wave_ir_text( Ok(Err(error)) => emit_backend_error_and_exit(file_path, &code, error), Err((msg, loc)) => { emit_codegen_panic_and_exit(file_path, &code, "llvm-ir-generation", msg, loc) - } + }, }; if debug.ir { @@ -758,7 +726,7 @@ unsafe fn emit_wave_codegen_file_from_hir( Ok(Err(error)) => emit_backend_error_and_exit(file_path, code, error), Err((msg, loc)) => { emit_codegen_panic_and_exit(file_path, code, "llvm-ir-generation", msg, loc) - } + }, }; println!("\n===== LLVM IR =====\n{}", ir); } @@ -782,11 +750,11 @@ unsafe fn emit_wave_codegen_file_from_hir( match run_panic_guarded(|| unsafe { emit_codegen_file(hir, opt_flag, &backend_opts, output, kind) }) { - Ok(Ok(())) => {} + Ok(Ok(())) => {}, Ok(Err(error)) => emit_backend_error_and_exit(file_path, code, error), Err((msg, loc)) => { emit_codegen_panic_and_exit(file_path, code, codegen_file_phase(kind), msg, loc) - } + }, } } @@ -798,15 +766,7 @@ pub(crate) unsafe fn emit_wave_bitcode_file( llvm: &LlvmFlags, output: &Path, ) { - emit_wave_codegen_file( - file_path, - opt_flag, - debug, - dep, - llvm, - output, - CodegenFileKind::Bitcode, - ); + emit_wave_codegen_file(file_path, opt_flag, debug, dep, llvm, output, CodegenFileKind::Bitcode); } pub(crate) unsafe fn emit_wave_assembly_file( @@ -850,7 +810,7 @@ pub(crate) unsafe fn run_wave_file( .with_help("check if the file exists and you have permission to read it") .display_auto(); process::exit(1); - } + }, }; let target = target_condition_context_for_llvm(Some(llvm)); let code = preprocess_target_attrs(&raw_code, &target); @@ -881,7 +841,7 @@ pub(crate) unsafe fn run_wave_file( Err(e) => { e.display_auto(); process::exit(1); - } + }, }; validate_expanded_ast_or_exit(&expanded); let ast = match monomorphize_generics(expanded.ast) { @@ -902,15 +862,13 @@ pub(crate) unsafe fn run_wave_file( ) .display_auto(); process::exit(1); - } + }, }; let hir = lower_wave_hir_or_exit(file_path, &code, ast); - let object_patch = utf8_output_path( - &default_output_path(file_path, Path::new(""), Some("o")), - file_path, - ); + let object_patch = + utf8_output_path(&default_output_path(file_path, Path::new(""), Some("o")), file_path); emit_wave_codegen_file_from_hir( file_path, &code, @@ -939,10 +897,8 @@ pub(crate) unsafe fn run_wave_file( println!(); } - let exe_patch = utf8_output_path( - &default_output_path(file_path, Path::new("target"), None), - file_path, - ); + let exe_patch = + utf8_output_path(&default_output_path(file_path, Path::new("target"), None), file_path); let backend_opts = build_backend_options(llvm); match run_panic_guarded(|| { @@ -954,7 +910,7 @@ pub(crate) unsafe fn run_wave_file( &backend_opts, ) }) { - Ok(Ok(())) => {} + Ok(Ok(())) => {}, Ok(Err(error)) => emit_backend_error_and_exit(file_path, &code, error), Err((msg, loc)) => emit_codegen_panic_and_exit(file_path, &code, "native-link", msg, loc), } @@ -1100,15 +1056,9 @@ pub(crate) unsafe fn build_wave_file( let backend_opts = build_backend_options(llvm); match run_panic_guarded(|| { - link_objects( - &[object_path], - &exe_path, - &link.libs, - &link.paths, - &backend_opts, - ) + link_objects(&[object_path], &exe_path, &link.libs, &link.paths, &backend_opts) }) { - Ok(Ok(())) => {} + Ok(Ok(())) => {}, Ok(Err(error)) => emit_backend_error_and_exit(file_path, &source, error), Err((msg, loc)) => emit_codegen_panic_and_exit(file_path, &source, "native-link", msg, loc), } diff --git a/src/std.rs b/src/std.rs index 6111d96b..e81c1495 100644 --- a/src/std.rs +++ b/src/std.rs @@ -56,9 +56,7 @@ fn select_std_reference<'a>( )); } if value.starts_with('-') || value.contains(':') || value.chars().any(char::is_whitespace) { - return Err(CliError::usage( - "invalid std reference: expected a Git commit, branch or tag", - )); + return Err(CliError::usage("invalid std reference: expected a Git commit, branch or tag")); } Ok(value) } @@ -141,17 +139,11 @@ fn fetch_std_from_wave_repo_sparse( // commit. Never fetch a moving master as a fallback for a missing pin. run_cmd(Command::new("git").arg("init").arg(checkout), "git init")?; run_cmd( - Command::new("git") - .arg("-C") - .arg(checkout) - .args(["remote", "add", "origin", repository]), + Command::new("git").arg("-C").arg(checkout).args(["remote", "add", "origin", repository]), "git remote add", )?; run_cmd( - Command::new("git") - .arg("-C") - .arg(checkout) - .args(["sparse-checkout", "set", "std"]), + Command::new("git").arg("-C").arg(checkout).args(["sparse-checkout", "set", "std"]), "git sparse-checkout set std", )?; run_cmd( @@ -165,19 +157,12 @@ fn fetch_std_from_wave_repo_sparse( "git fetch std reference", )?; run_cmd( - Command::new("git") - .arg("-C") - .arg(checkout) - .args(["checkout", "--detach", "FETCH_HEAD"]), + Command::new("git").arg("-C").arg(checkout).args(["checkout", "--detach", "FETCH_HEAD"]), "git checkout std revision", )?; let source_revision = run_cmd_stdout( - Command::new("git") - .arg("-C") - .arg(checkout) - .arg("rev-parse") - .arg("HEAD"), + Command::new("git").arg("-C").arg(checkout).arg("rev-parse").arg("HEAD"), "git rev-parse HEAD", )?; @@ -333,10 +318,7 @@ fn run_cmd_stdout(cmd: &mut Command, label: &str) -> Result { let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string(); if stdout.is_empty() { - return Err(CliError::CommandFailed(format!( - "{} returned empty output", - label - ))); + return Err(CliError::CommandFailed(format!("{} returned empty output", label))); } Ok(stdout) } @@ -346,10 +328,7 @@ fn make_tmp_dir(prefix: &str) -> Result { } fn unique_path_in(parent: &Path, prefix: &str) -> PathBuf { - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos(); parent.join(format!("{}-{}-{}", prefix, std::process::id(), timestamp)) } @@ -400,37 +379,26 @@ mod tests { git(&["commit", "-m", "compatible"]); let pinned = git(&["rev-parse", "HEAD"]); git(&["tag", "compatible"]); - fs::write( - &manifest, - r#"{"name":"std","format":1,"compatibility_revision":999999}"#, - ) - .unwrap(); + fs::write(&manifest, r#"{"name":"std","format":1,"compatibility_revision":999999}"#) + .unwrap(); fs::write(repository.join("std/version.wave"), "new").unwrap(); git(&["add", "."]); git(&["commit", "-m", "incompatible"]); let installed = root.join("install/std"); let repo = repository.to_str().unwrap(); install_from_repository(repo, &pinned, &installed, |_, _| Ok(())).unwrap(); - assert_eq!( - fs::read_to_string(installed.join("version.wave")).unwrap(), - "old" - ); + assert_eq!(fs::read_to_string(installed.join("version.wave")).unwrap(), "old"); assert!(fs::read_to_string(installed.join("INSTALL_META")) .unwrap() .contains(&format!("revision={pinned}"))); for reference in ["HEAD", "missing-reference"] { assert!(install_from_repository(repo, reference, &installed, |_, _| Ok(())).is_err()); - assert_eq!( - fs::read_to_string(installed.join("version.wave")).unwrap(), - "old" - ); + assert_eq!(fs::read_to_string(installed.join("version.wave")).unwrap(), "old"); } - assert!( - install_from_repository(repo, "compatible", &installed, |_, _| Err( - CliError::CommandFailed("validation failed".into()) - )) - .is_err() - ); + assert!(install_from_repository(repo, "compatible", &installed, |_, _| Err( + CliError::CommandFailed("validation failed".into()) + )) + .is_err()); assert!(install_from_repository( root.join("missing-repository").to_str().unwrap(), &pinned, @@ -438,14 +406,8 @@ mod tests { |_, _| Ok(()) ) .is_err()); - assert_eq!( - fs::read_to_string(installed.join("version.wave")).unwrap(), - "old" - ); - assert_eq!( - fs::read_dir(installed.parent().unwrap()).unwrap().count(), - 1 - ); + assert_eq!(fs::read_to_string(installed.join("version.wave")).unwrap(), "old"); + assert_eq!(fs::read_dir(installed.parent().unwrap()).unwrap().count(), 1); install_from_repository(repo, "compatible", &installed, |_, stage| { assert_eq!(fs::read_to_string(stage.join("version.wave"))?, "old"); Ok(()) @@ -461,14 +423,8 @@ mod tests { let revision = "0123456789abcdef0123456789abcdef01234567"; assert_eq!(select_std_reference(None, revision).unwrap(), revision); assert!(select_std_reference(None, "").is_err()); - assert_eq!( - select_std_reference(Some("release-tag"), "").unwrap(), - "release-tag" - ); - assert_eq!( - resolve_std_reference(Some("release-tag")).unwrap(), - "release-tag" - ); + assert_eq!(select_std_reference(Some("release-tag"), "").unwrap(), "release-tag"); + assert_eq!(resolve_std_reference(Some("release-tag")).unwrap(), "release-tag"); for value in ["", "--all", "head:local", "two refs"] { assert!(resolve_std_reference(Some(value)).is_err()); } @@ -487,10 +443,7 @@ mod tests { replace_std_tree(&staged, &installed).unwrap(); assert!(!installed.join("old.wave").exists()); - assert_eq!( - fs::read_to_string(installed.join("new.wave")).unwrap(), - "new" - ); + assert_eq!(fs::read_to_string(installed.join("new.wave")).unwrap(), "new"); assert!(!staged.exists()); let _ = fs::remove_dir_all(root); } @@ -505,13 +458,8 @@ mod tests { let error = replace_std_tree(&missing_stage, &installed).unwrap_err(); - assert!(error - .message() - .contains("previous installation was restored")); - assert_eq!( - fs::read_to_string(installed.join("old.wave")).unwrap(), - "old" - ); + assert!(error.message().contains("previous installation was restored")); + assert_eq!(fs::read_to_string(installed.join("old.wave")).unwrap(), "old"); let _ = fs::remove_dir_all(root); } } diff --git a/src/version.rs b/src/version.rs index 1f831d49..51086a83 100644 --- a/src/version.rs +++ b/src/version.rs @@ -32,10 +32,7 @@ pub fn get_os_pretty_name() -> String { if let Ok(content) = fs::read_to_string("/etc/os-release") { for line in content.lines() { if line.starts_with("PRETTY_NAME=") { - return line - .trim_start_matches("PRETTY_NAME=") - .trim_matches('"') - .to_string(); + return line.trim_start_matches("PRETTY_NAME=").trim_matches('"').to_string(); } } } diff --git a/tests/ast_serialization.rs b/tests/ast_serialization.rs index 1ceaccc9..e24b3dd0 100644 --- a/tests/ast_serialization.rs +++ b/tests/ast_serialization.rs @@ -28,22 +28,14 @@ fun main() -> i32 { .arg(&source) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); let text = fs::read_to_string(root.join(format!("sample.{extension}"))).unwrap(); if format == "sexpr" { assert!(text.starts_with("(ast\n (schema_version 1)")); assert!(text.contains("(function")); } else { - let value = if format == "json" { - wson::parse_json(&text) - } else { - wson::loads(&text) - } - .unwrap(); + let value = if format == "json" { wson::parse_json(&text) } else { wson::loads(&text) } + .unwrap(); assert_eq!(value.get_u64("schema_version"), Some(1)); assert_eq!(value.get_str("stage"), Some("parsed")); assert!(text.contains("340282366920938463463374607431768211456")); @@ -69,30 +61,19 @@ fn parsed_ast_schema_snapshots_are_deterministic() { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let output = std::env::temp_dir().join(format!("wave ast snapshots {}", std::process::id())); fs::create_dir_all(&output).unwrap(); - for (format, extension) in [ - (None, "ast.wson"), - (Some("json"), "ast.json"), - (Some("sexpr"), "ast"), - ] { + for (format, extension) in + [(None, "ast.wson"), (Some("json"), "ast.json"), (Some("sexpr"), "ast")] + { let mut command = Command::new(env!("CARGO_BIN_EXE_wavec")); command .current_dir(root) - .args([ - "build", - "tests/fixtures/ast/parsed.wave", - "--emit=ast", - "--out-dir", - ]) + .args(["build", "tests/fixtures/ast/parsed.wave", "--emit=ast", "--out-dir"]) .arg(&output); if let Some(format) = format { command.args(["--ast-format", format]); } let result = command.output().unwrap(); - assert!( - result.status.success(), - "{}", - String::from_utf8_lossy(&result.stderr) - ); + assert!(result.status.success(), "{}", String::from_utf8_lossy(&result.stderr)); let actual = fs::read_to_string(output.join(format!("parsed.{extension}"))).unwrap(); let expected = fs::read_to_string(root.join(format!("tests/fixtures/ast/parsed.{extension}"))) diff --git a/tests/boundary_regressions.rs b/tests/boundary_regressions.rs index b7b31186..3629299c 100644 --- a/tests/boundary_regressions.rs +++ b/tests/boundary_regressions.rs @@ -41,13 +41,13 @@ impl Case { ); Self { root, home } } + fn command(&self) -> Command { let mut cmd = Command::new(env!("CARGO_BIN_EXE_wavec")); - cmd.env("HOME", &self.home) - .env("NO_COLOR", "1") - .current_dir(&self.root); + cmd.env("HOME", &self.home).env("NO_COLOR", "1").current_dir(&self.root); cmd } + fn run(&self, source: &Path) -> String { for opt in ["-O0", "-O2"] { success( @@ -64,10 +64,7 @@ impl Case { ); } fs::read_to_string( - self.root - .join("-O0") - .join(source.file_stem().unwrap()) - .with_extension("ll"), + self.root.join("-O0").join(source.file_stem().unwrap()).with_extension("ll"), ) .unwrap() } @@ -86,9 +83,11 @@ fn success(output: Output) { String::from_utf8_lossy(&output.stderr) ); } + fn source(relative: &str) -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join(relative) } + fn shared(number: u32) -> String { Case::new(&number.to_string()).run(&source(&format!("tests/cases/shared/test{number}.wave"))) } @@ -138,10 +137,7 @@ fn float_inequality_is_unordered_in_all_numeric_paths() { #[test] fn floating_negation_preserves_sign_and_nan_payloads() { let ir = shared(130); - assert!( - ir.contains("fneg float") && ir.contains("fneg double"), - "{ir}" - ); + assert!(ir.contains("fneg float") && ir.contains("fneg double"), "{ir}"); } #[test] fn dirname_fallback_preserves_guard_bytes() { diff --git a/tests/checked_std_regressions.rs b/tests/checked_std_regressions.rs index 3c6b6734..a93ddf4e 100644 --- a/tests/checked_std_regressions.rs +++ b/tests/checked_std_regressions.rs @@ -14,9 +14,7 @@ fn copy_std(source: &Path, destination: &Path) { } else if entry.path().extension().is_some_and(|e| e == "wave") { fs::write( to, - fs::read_to_string(entry.path()) - .unwrap() - .replace("\"std::", "\"checkout_std::"), + fs::read_to_string(entry.path()).unwrap().replace("\"std::", "\"checkout_std::"), ) .unwrap(); } else { @@ -41,9 +39,7 @@ fn checked_typed_memory_and_sleb128_preserve_data_and_failure_state() { let source = dir.join(format!("{name}.wave")); fs::write( &source, - fs::read_to_string(root.join(fixture)) - .unwrap() - .replace("\"std::", "\"checkout_std::"), + fs::read_to_string(root.join(fixture)).unwrap().replace("\"std::", "\"checkout_std::"), ) .unwrap(); for opt in ["-O0", "-O2"] { @@ -105,15 +101,7 @@ fn run_cross_fixture( Command::new(env!("CARGO_BIN_EXE_wavec")) .arg("build") .arg(source) - .args([ - "--dep", - dep, - opt, - "--target", - target, - "--emit=obj", - "--out-dir", - ]) + .args(["--dep", dep, opt, "--target", target, "--emit=obj", "--out-dir"]) .arg(&out), ); let object = out.join(format!("{name}.o")); diff --git a/tests/codegen_regressions.rs b/tests/codegen_regressions.rs index 4a7bf357..955bfc5f 100644 --- a/tests/codegen_regressions.rs +++ b/tests/codegen_regressions.rs @@ -31,12 +31,8 @@ fn wavec_bin() -> PathBuf { fn temp_case_dir(name: &str) -> PathBuf { let sequence = NEXT_TEMP_CASE.fetch_add(1, Ordering::Relaxed); - let dir = std::env::temp_dir().join(format!( - "wavec-{}-{}-{}", - name, - std::process::id(), - sequence - )); + let dir = + std::env::temp_dir().join(format!("wavec-{}-{}-{}", name, std::process::id(), sequence)); let _ = fs::remove_dir_all(&dir); fs::create_dir_all(&dir).unwrap(); dir @@ -164,14 +160,8 @@ fn compact_variants_preserve_constants_statics_and_nested_payloads() { dir.join(target).as_os_str(), ]); let ir = fs::read_to_string(dir.join(target).join("test126.ll")).unwrap(); - let layout = ir - .lines() - .find(|line| line.starts_with("%variant.Payload =")) - .unwrap(); - assert!( - layout.contains("[32 x i8]") && layout.contains("[0 x"), - "{target}: {layout}" - ); + let layout = ir.lines().find(|line| line.starts_with("%variant.Payload =")).unwrap(); + assert!(layout.contains("[32 x i8]") && layout.contains("[0 x"), "{target}: {layout}"); } } @@ -179,10 +169,7 @@ fn compact_variants_preserve_constants_statics_and_nested_payloads() { fn type_layout_queries_follow_the_target_and_generic_storage() { let dir = temp_case_dir("type-layout-queries"); let home = dir.join("home"); - copy_tree( - &Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), - &home.join(".wave/lib/wave/std"), - ); + copy_tree(&Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std")); let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cases/shared/test124.wave"); for optimization in ["-O0", "-O2"] { let output = wavec_command() @@ -195,11 +182,7 @@ fn type_layout_queries_follow_the_target_and_generic_storage() { .arg(dir.join(optimization)) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); } let source = write_wave( &dir, @@ -240,17 +223,9 @@ fun main() -> i32 { return 0; } ("record_size", 16), ("array_size", 48), ] { - let body = ir - .split(&format!("@{function}(")) - .nth(1) - .unwrap() - .split('}') - .next() - .unwrap(); - assert!( - body.contains(&format!("ret i64 {value}")), - "{target}: {function}: {body}" - ); + let body = + ir.split(&format!("@{function}(")).nth(1).unwrap().split('}').next().unwrap(); + assert!(body.contains(&format!("ret i64 {value}")), "{target}: {function}: {body}"); } } for query in [ @@ -262,10 +237,7 @@ fun main() -> i32 { return 0; } let source = write_wave(&dir, "invalid.wave", &format!("fun main() {{ {query}; }}")); let output = wavec_command().arg("check").arg(source).output().unwrap(); let stderr = String::from_utf8_lossy(&output.stderr); - assert!( - !output.status.success() && !stderr.contains("panicked"), - "{query}: {stderr}" - ); + assert!(!output.status.success() && !stderr.contains("panicked"), "{query}: {stderr}"); } } @@ -273,10 +245,7 @@ fun main() -> i32 { return 0; } fn uleb128_cursors_preserve_state_on_failure() { let dir = temp_case_dir("uleb128-cursors"); let home = dir.join("home"); - copy_tree( - &Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), - &home.join(".wave/lib/wave/std"), - ); + copy_tree(&Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std")); let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cases/shared/test125.wave"); for optimization in ["-O0", "-O2"] { let output = wavec_command() @@ -289,11 +258,7 @@ fn uleb128_cursors_preserve_state_on_failure() { .arg(dir.join(optimization)) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); } } @@ -313,11 +278,7 @@ fn os_randomness_preserves_bounds_and_partial_failure_counts() { .arg(dir.join("native")) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); fs::write( std.join("sys/random.wave"), r#" @@ -356,21 +317,14 @@ fun main() -> i32 { .arg(dir.join("partial")) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); } #[test] fn linux_numeric_and_explicit_table_resolvers_are_libc_independent() { let dir = temp_case_dir("linux-numeric-resolver"); let home = dir.join("home"); - copy_tree( - &Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), - &home.join(".wave/lib/wave/std"), - ); + copy_tree(&Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std")); let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/linux_resolver"); for target in [ "x86_64-unknown-linux-gnu", @@ -393,38 +347,22 @@ fn linux_numeric_and_explicit_table_resolvers_are_libc_independent() { .arg(dir.join(target)) .output() .unwrap(); - assert!( - output.status.success(), - "{target}: {}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{target}: {}", String::from_utf8_lossy(&output.stderr)); let ir = fs::read_to_string(dir.join(target).join("numeric_and_table.ll")).unwrap(); for symbol in ["getaddrinfo", "freeaddrinfo", "getpagesize"] { - assert!( - !ir.contains(&format!("@{symbol}(")), - "unexpected libc import {symbol}" - ); + assert!(!ir.contains(&format!("@{symbol}(")), "unexpected libc import {symbol}"); } } if cfg!(all(target_os = "linux", target_arch = "x86_64")) { let runtime = dir.join("start.o"); let compile = Command::new("cc") - .args([ - "-ffreestanding", - "-fno-builtin", - "-fno-stack-protector", - "-c", - ]) + .args(["-ffreestanding", "-fno-builtin", "-fno-stack-protector", "-c"]) .arg(fixture.join("start.c")) .arg("-o") .arg(&runtime) .output() .unwrap(); - assert!( - compile.status.success(), - "{}", - String::from_utf8_lossy(&compile.stderr) - ); + assert!(compile.status.success(), "{}", String::from_utf8_lossy(&compile.stderr)); let executable = dir.join("resolver"); let link = Command::new("cc") .args(["-nostdlib", "-static", "-Wl,-e,_start"]) @@ -434,11 +372,7 @@ fn linux_numeric_and_explicit_table_resolvers_are_libc_independent() { .arg(&executable) .output() .unwrap(); - assert!( - link.status.success(), - "{}", - String::from_utf8_lossy(&link.stderr) - ); + assert!(link.status.success(), "{}", String::from_utf8_lossy(&link.stderr)); assert!(Command::new(executable).status().unwrap().success()); } } @@ -447,10 +381,7 @@ fn linux_numeric_and_explicit_table_resolvers_are_libc_independent() { fn windows_filesystem_errors_and_unicode_paths_use_native_apis() { let dir = temp_case_dir("windows-filesystem"); let home = dir.join("home"); - copy_tree( - &Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), - &home.join(".wave/lib/wave/std"), - ); + copy_tree(&Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std")); for fixture in ["errors", "unicode"] { let source = Path::new(env!("CARGO_MANIFEST_DIR")) .join(format!("tests/fixtures/windows_fs/{fixture}.wave")); @@ -476,12 +407,9 @@ fn windows_filesystem_errors_and_unicode_paths_use_native_apis() { String::from_utf8_lossy(&output.stderr) ); let ir = fs::read_to_string(destination.join(format!("{fixture}.ll"))).unwrap(); - for symbol in [ - "CreateFileW", - "MultiByteToWideChar", - "WideCharToMultiByte", - "GetLastError", - ] { + for symbol in + ["CreateFileW", "MultiByteToWideChar", "WideCharToMultiByte", "GetLastError"] + { assert!(ir.contains(&format!("@{symbol}(")), "missing {symbol}"); } assert!(!ir.contains("@CreateFileA(")); @@ -496,11 +424,7 @@ fn windows_filesystem_errors_and_unicode_paths_use_native_apis() { .arg(&output) .output() .unwrap(); - assert!( - compile.status.success(), - "{}", - String::from_utf8_lossy(&compile.stderr) - ); + assert!(compile.status.success(), "{}", String::from_utf8_lossy(&compile.stderr)); let run = Command::new(output).current_dir(&dir).output().unwrap(); assert!( run.status.success(), @@ -517,30 +441,13 @@ fn windows_filesystem_errors_and_unicode_paths_use_native_apis() { fn wasm_reclaiming_allocators_emit_for_both_pointer_widths() { let dir = temp_case_dir("wasm-reclaiming-allocators"); let home = dir.join("home"); - copy_tree( - &Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), - &home.join(".wave/lib/wave/std"), - ); + copy_tree(&Path::new(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std")); for (target, case, intrinsic) in [ - ( - "wasm32-unknown-unknown", - "wasm/wasm32/test11.wave", - "llvm.wasm.memory.grow.i32", - ), - ( - "wasm32-wasip1", - "wasi/wasm32/test11.wave", - "llvm.wasm.memory.grow.i32", - ), - ( - "wasm64-unknown-unknown", - "wasm/wasm64/test12.wave", - "llvm.wasm.memory.grow.i64", - ), + ("wasm32-unknown-unknown", "wasm/wasm32/test11.wave", "llvm.wasm.memory.grow.i32"), + ("wasm32-wasip1", "wasi/wasm32/test11.wave", "llvm.wasm.memory.grow.i32"), + ("wasm64-unknown-unknown", "wasm/wasm64/test12.wave", "llvm.wasm.memory.grow.i64"), ] { - let source = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests/cases") - .join(case); + let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cases").join(case); let output = wavec_command() .env("HOME", &home) .arg("build") @@ -552,15 +459,9 @@ fn wasm_reclaiming_allocators_emit_for_both_pointer_widths() { .arg(dir.join(target)) .output() .unwrap(); - assert!( - output.status.success(), - "{target}: {}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{target}: {}", String::from_utf8_lossy(&output.stderr)); let ir = fs::read_to_string( - dir.join(target) - .join(Path::new(case).file_stem().unwrap()) - .with_extension("ll"), + dir.join(target).join(Path::new(case).file_stem().unwrap()).with_extension("ll"), ) .unwrap(); assert!(ir.contains(intrinsic), "{target}: {ir}"); @@ -664,10 +565,7 @@ fun main() -> i32 { return get_current_process_id() as i32; } assert!(ir.contains("@GetCurrentProcessId"), "{target}: {ir}"); } - if cfg!(any( - feature = "llvm-target-all", - feature = "llvm-target-aarch64" - )) { + if cfg!(any(feature = "llvm-target-all", feature = "llvm-target-aarch64")) { let arm64_bin = dir.join("system-arm64.exe"); let (link_plan, stderr) = run_wavec_capture([ OsStr::new("build"), @@ -681,10 +579,7 @@ fun main() -> i32 { return get_current_process_id() as i32; } assert!(stderr.trim().is_empty(), "{stderr}"); assert!(link_plan.contains("lld-link"), "{link_plan}"); } - if !cfg!(any( - feature = "llvm-target-all", - feature = "llvm-target-x86" - )) { + if !cfg!(any(feature = "llvm-target-all", feature = "llvm-target-x86")) { return; } let linux_out = dir.join("linux"); @@ -705,39 +600,22 @@ fn incompatible_std_is_rejected_from_an_isolated_home() { let home = dir.join("home"); let std_root = home.join(".wave/lib/wave/std"); fs::create_dir_all(std_root.join("string")).unwrap(); - fs::write( - std_root.join("manifest.json"), - r#"{"name":"std","compatibility_revision":0}"#, - ) - .unwrap(); - fs::write( - std_root.join("string/len.wave"), - "pub fun len(value: str) -> i64 { return 0; }\n", - ) - .unwrap(); + fs::write(std_root.join("manifest.json"), r#"{"name":"std","compatibility_revision":0}"#) + .unwrap(); + fs::write(std_root.join("string/len.wave"), "pub fun len(value: str) -> i64 { return 0; }\n") + .unwrap(); let source = write_wave( &dir, "main.wave", "import(\"std::string::len\")::{len};\nfun main() { len(\"x\"); }\n", ); - let output = wavec_command() - .env("HOME", &home) - .arg("check") - .arg(&source) - .output() - .unwrap(); + let output = wavec_command().env("HOME", &home).arg("check").arg(&source).output().unwrap(); assert!(!output.status.success()); let error = String::from_utf8_lossy(&output.stderr); + assert!(error.contains("installed std compatibility revision 0"), "{error}"); assert!( - error.contains("installed std compatibility revision 0"), - "{error}" - ); - assert!( - error.contains(&format!( - "requires {}", - parser::import::STD_COMPATIBILITY_REVISION - )), + error.contains(&format!("requires {}", parser::import::STD_COMPATIBILITY_REVISION)), "{error}" ); assert!(error.contains("wavec update std"), "{error}"); @@ -829,18 +707,12 @@ fun infer_nested() { } fn bytes_contains(haystack: &[u8], needle: &[u8]) -> bool { - haystack - .windows(needle.len()) - .any(|window| window == needle) + haystack.windows(needle.len()).any(|window| window == needle) } fn riscv64_elf_flags(path: &Path) -> u32 { let object = fs::read(path).unwrap(); - assert!( - object.len() >= 52, - "truncated ELF object: {}", - path.display() - ); + assert!(object.len() >= 52, "truncated ELF object: {}", path.display()); assert_eq!(&object[..4], b"\x7fELF", "{}", path.display()); assert_eq!(object[4], 2, "expected ELF64 object: {}", path.display()); assert_eq!(u16::from_le_bytes([object[18], object[19]]), 243); @@ -850,11 +722,7 @@ fn riscv64_elf_flags(path: &Path) -> u32 { #[cfg(any(feature = "llvm-target-all", feature = "llvm-target-loongarch"))] fn loongarch64_elf_flags(path: &Path) -> u32 { let object = fs::read(path).unwrap(); - assert!( - object.len() >= 52, - "truncated ELF object: {}", - path.display() - ); + assert!(object.len() >= 52, "truncated ELF object: {}", path.display()); assert_eq!(&object[..4], b"\x7fELF", "{}", path.display()); assert_eq!(object[4], 2, "expected ELF64 object: {}", path.display()); assert_eq!(u16::from_le_bytes([object[18], object[19]]), 258); @@ -936,14 +804,8 @@ fn retired_let_declarations_are_rejected() { let dir = temp_case_dir("retired-let-syntax"); let cases = [ ("let.wave", "fun main() { let value: i32 = 1; }\n"), - ( - "let_mut.wave", - "fun main() { let mut value: i32 = 1; value += 1; }\n", - ), - ( - "for_let.wave", - "fun main() { for (let index: i32 = 0; index < 1; index += 1) {} }\n", - ), + ("let_mut.wave", "fun main() { let mut value: i32 = 1; value += 1; }\n"), + ("for_let.wave", "fun main() { for (let index: i32 = 0; index < 1; index += 1) {} }\n"), ]; for (file_name, source) in cases { @@ -1012,12 +874,7 @@ fun main() { "#, ); let mapping = OsString::from(format!("add={}", package.display())); - run_wavec([ - OsStr::new("check"), - entry.as_os_str(), - OsStr::new("--dep"), - mapping.as_os_str(), - ]); + run_wavec([OsStr::new("check"), entry.as_os_str(), OsStr::new("--dep"), mapping.as_os_str()]); let selected_private = write_wave( &dir, @@ -1030,11 +887,7 @@ fun main() { OsStr::new("--dep"), mapping.as_os_str(), ]); - assert!( - error.contains("symbol 'internal_sum' is private in module 'add'"), - "{}", - error - ); + assert!(error.contains("symbol 'internal_sum' is private in module 'add'"), "{}", error); let qualified_private = write_wave( &dir, @@ -1047,11 +900,7 @@ fun main() { OsStr::new("--dep"), mapping.as_os_str(), ]); - assert!( - error.contains("symbol 'internal_sum' is private in module 'add'"), - "{}", - error - ); + assert!(error.contains("symbol 'internal_sum' is private in module 'add'"), "{}", error); } #[test] @@ -1117,10 +966,7 @@ fun main() -> i32 { return logical_shift(0x80000000 as u32) as i32; } dir.as_os_str(), ]); let ir = fs::read_to_string(dir.join("shift.ll")).unwrap(); - assert!( - ir.contains("lshr i32"), - "unsigned shift must use lshr:\n{ir}" - ); + assert!(ir.contains("lshr i32"), "unsigned shift must use lshr:\n{ir}"); assert!(ir.contains("ashr i32"), "signed shift must use ashr:\n{ir}"); } @@ -1171,24 +1017,15 @@ fun main() -> i32 { ]); let ir = fs::read_to_string(dir.join("integer_ops.ll")).unwrap(); for instruction in ["udiv i32", "urem i32", "icmp ugt i32"] { - assert!( - ir.contains(instruction), - "unsigned integers must use {instruction}:\n{ir}" - ); + assert!(ir.contains(instruction), "unsigned integers must use {instruction}:\n{ir}"); } - assert!( - ir.contains("zext i1"), - "boolean comparison results must normalize true to one:\n{ir}" - ); + assert!(ir.contains("zext i1"), "boolean comparison results must normalize true to one:\n{ir}"); assert!( ir.contains("lshr i16"), "nested u16 bitwise expressions must retain unsigned shift semantics:\n{ir}" ); for instruction in ["sdiv i32", "srem i32", "icmp sgt i32"] { - assert!( - ir.contains(instruction), - "signed integers must use {instruction}:\n{ir}" - ); + assert!(ir.contains(instruction), "signed integers must use {instruction}:\n{ir}"); } run_native_wave(&source); @@ -1198,10 +1035,7 @@ fun main() -> i32 { "fun mask(value: u16) -> u16 { return value & 0x10000; }\nfun main() {}\n", ); let error = run_wavec_expect_failure([OsStr::new("check"), invalid_literal.as_os_str()]); - assert!( - error.contains("integer literal `0x10000` does not fit `u16`"), - "{error}" - ); + assert!(error.contains("integer literal `0x10000` does not fit `u16`"), "{error}"); } #[test] @@ -1230,14 +1064,8 @@ fun main() -> i32 { dir.as_os_str(), ]); let ir = fs::read_to_string(dir.join("widen.ll")).unwrap(); - assert!( - ir.contains("zext i8"), - "unsigned widening must use zext:\n{ir}" - ); - assert!( - ir.contains("sext i8"), - "signed widening must use sext:\n{ir}" - ); + assert!(ir.contains("zext i8"), "unsigned widening must use zext:\n{ir}"); + assert!(ir.contains("sext i8"), "signed widening must use sext:\n{ir}"); assert!( ir.contains("ret i64 9221120237041090560"), "an explicitly cast wide literal must retain all target bits:\n{ir}" @@ -1386,15 +1214,8 @@ fun main() -> i32 { ir.matches("zext i8").count() >= 4, "implicit unsigned widening must use zext in every value context:\n{ir}" ); - assert!( - ir.contains("idx_zext"), - "unsigned array index must zero-extend:\n{ir}" - ); - for (function, width) in [ - ("add_offset", 8), - ("add_offset_left", 8), - ("add_offset_u32", 32), - ] { + assert!(ir.contains("idx_zext"), "unsigned array index must zero-extend:\n{ir}"); + for (function, width) in [("add_offset", 8), ("add_offset_left", 8), ("add_offset_u32", 32)] { let body = ir .split(&format!("define ptr @{function}(")) .nth(1) @@ -1529,11 +1350,7 @@ fun main() -> i32 { .unwrap(); child.stdin.take().unwrap().write_all(b"73\n").unwrap(); let output = child.wait_with_output().unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); } #[test] @@ -1564,10 +1381,7 @@ fun main() -> i32 { "#, ); let mut targets = vec![("x86_64-unknown-linux-gnu", 64)]; - if cfg!(any( - feature = "llvm-target-all", - feature = "llvm-target-wasm" - )) { + if cfg!(any(feature = "llvm-target-all", feature = "llvm-target-wasm")) { targets.push(("wasm32-unknown-unknown", 32)); targets.push(("wasm64-unknown-unknown", 64)); } @@ -1588,34 +1402,17 @@ fun main() -> i32 { ("narrow_unsigned", "zext", "i8"), ("widest_unsigned", "trunc", "i128"), ] { - let body = ir - .split(&format!("@{function}(")) - .nth(1) - .unwrap() - .split('}') - .next() - .unwrap(); + let body = + ir.split(&format!("@{function}(")).nth(1).unwrap().split('}').next().unwrap(); assert!( body.contains(&format!("{instruction} {operand}")) && body.contains(&format!("to i{width}")), "{target}: {body}" ); } - let body = ir - .split("@word_unsigned(") - .nth(1) - .unwrap() - .split('}') - .next() - .unwrap(); + let body = ir.split("@word_unsigned(").nth(1).unwrap().split('}').next().unwrap(); assert_eq!(body.contains("zext i32"), width == 64, "{target}: {body}"); - let body = ir - .split("@wide_unsigned(") - .nth(1) - .unwrap() - .split('}') - .next() - .unwrap(); + let body = ir.split("@wide_unsigned(").nth(1).unwrap().split('}').next().unwrap(); assert_eq!(body.contains("trunc i64"), width == 32, "{target}: {body}"); } } @@ -1673,10 +1470,7 @@ fun main() -> i32 { dir.as_os_str(), ]); let ir = fs::read_to_string(dir.join("compare.ll")).unwrap(); - assert!( - ir.contains("icmp eq i64"), - "numeric comparisons must retain the operand width:\n{ir}" - ); + assert!(ir.contains("icmp eq i64"), "numeric comparisons must retain the operand width:\n{ir}"); run_native_wave(&source); } @@ -1687,24 +1481,9 @@ fn freebsd_lp64_providers_emit_direct_syscalls_and_kernel_layouts() { let home = dir.join("home"); copy_tree(&root.join("std"), &home.join(".wave/lib/wave/std")); for (arch, target, instruction, registers) in [ - ( - "amd64", - "x86_64-unknown-freebsd", - "syscall", - ["={rax}", "={rdx}"], - ), - ( - "arm64", - "aarch64-unknown-freebsd", - "svc $3", - ["={x0}", "={x1}"], - ), - ( - "riscv64", - "riscv64-unknown-freebsd", - "ecall", - ["={x10}", "={x11}"], - ), + ("amd64", "x86_64-unknown-freebsd", "syscall", ["={rax}", "={rdx}"]), + ("arm64", "aarch64-unknown-freebsd", "svc $3", ["={x0}", "={x1}"]), + ("riscv64", "riscv64-unknown-freebsd", "ecall", ["={x10}", "={x11}"]), ] { for number in [11, 12] { let source = root.join(format!("tests/cases/freebsd/{arch}/test{number}.wave")); @@ -1733,10 +1512,7 @@ fn freebsd_lp64_providers_emit_direct_syscalls_and_kernel_layouts() { ); } let ir = fs::read_to_string(output_dir.join(format!("test{number}.ll"))).unwrap(); - assert!( - ir.contains(instruction), - "{target} must issue a kernel trap" - ); + assert!(ir.contains(instruction), "{target} must issue a kernel trap"); for register in registers { assert!( ir.contains(register), @@ -1771,10 +1547,7 @@ fn std_net_compiles_for_every_supported_socket_abi() { let dir = temp_case_dir("std-net-target-matrix"); let home = dir.join("home"); let std_destination = home.join(".wave/lib/wave/std"); - copy_tree( - &PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("std"), - &std_destination, - ); + copy_tree(&PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("std"), &std_destination); let sources = [ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/std/net_tcp.wave"), @@ -1896,20 +1669,13 @@ fn std_net_compiles_for_every_supported_socket_abi() { "{target} must use kernel syscalls, not {obsolete}" ); } - let create_number = if target.starts_with("x86_64-") { - 291 - } else { - 20 - }; + let create_number = if target.starts_with("x86_64-") { 291 } else { 20 }; assert!( ir.contains(&format!("_syscall1(i64 {create_number}, i64 0)")), "{target} must call epoll_create1 through its native syscall number" ); - let (stride, data_offset) = if target.starts_with("x86_64-") { - (12, 4) - } else { - (16, 8) - }; + let (stride, data_offset) = + if target.starts_with("x86_64-") { (12, 4) } else { (16, 8) }; assert!( ir.contains(&format!( "__epoll_event_stride() #0 {{\nentry:\n ret i32 {stride}" @@ -1931,54 +1697,30 @@ fn std_net_compiles_for_every_supported_socket_abi() { #[test] fn import_graph_rejects_cycles_path_escape_and_public_main() { let dir = temp_case_dir("module-import-errors"); - fs::write( - dir.join("a.wave"), - "import(\"./b\");\npub fun from_a() -> i32 { return 1; }\n", - ) - .unwrap(); - fs::write( - dir.join("b.wave"), - "import(\"./a\");\npub fun from_b() -> i32 { return 2; }\n", - ) - .unwrap(); + fs::write(dir.join("a.wave"), "import(\"./b\");\npub fun from_a() -> i32 { return 1; }\n") + .unwrap(); + fs::write(dir.join("b.wave"), "import(\"./a\");\npub fun from_b() -> i32 { return 2; }\n") + .unwrap(); let cycle = write_wave(&dir, "cycle.wave", "import(\"./a\");\nfun main() {}\n"); let error = run_wavec_expect_failure([OsStr::new("check"), cycle.as_os_str()]); assert!(error.contains("import cycle detected"), "{}", error); - let escape = write_wave( - &dir, - "escape.wave", - "import(\"./../outside\");\nfun main() {}\n", - ); + let escape = write_wave(&dir, "escape.wave", "import(\"./../outside\");\nfun main() {}\n"); let error = run_wavec_expect_failure([OsStr::new("check"), escape.as_os_str()]); assert!(error.contains("escapes its module directory"), "{}", error); - let package_escape = write_wave( - &dir, - "package_escape.wave", - "import(\"add::..\");\nfun main() {}\n", - ); + let package_escape = + write_wave(&dir, "package_escape.wave", "import(\"add::..\");\nfun main() {}\n"); let error = run_wavec_expect_failure([OsStr::new("check"), package_escape.as_os_str()]); - assert!( - error.contains("package and module names must be identifiers"), - "{}", - error - ); + assert!(error.contains("package and module names must be identifiers"), "{}", error); let public_main = write_wave(&dir, "public_main.wave", "pub fun main() {}\n"); let error = run_wavec_expect_failure([OsStr::new("check"), public_main.as_os_str()]); - assert!( - error.contains("entry function `main` cannot be public"), - "{}", - error - ); + assert!(error.contains("entry function `main` cannot be public"), "{}", error); fs::write(dir.join("library_main.wave"), "fun main() {}\n").unwrap(); - let imported_main = write_wave( - &dir, - "imported_main.wave", - "import(\"./library_main\");\nfun main() {}\n", - ); + let imported_main = + write_wave(&dir, "imported_main.wave", "import(\"./library_main\");\nfun main() {}\n"); let error = run_wavec_expect_failure([OsStr::new("check"), imported_main.as_os_str()]); assert!( error.contains("function 'main' may only be declared in the entry module"), @@ -2104,20 +1846,8 @@ fun main() {} mode ); let stderr = String::from_utf8_lossy(&output.stderr); - assert!( - stderr.contains("error[E3001]"), - "{} ({}): {}", - file_name, - mode, - stderr - ); - assert!( - stderr.contains(expected), - "{} ({}): {}", - file_name, - mode, - stderr - ); + assert!(stderr.contains("error[E3001]"), "{} ({}): {}", file_name, mode, stderr); + assert!(stderr.contains(expected), "{} ({}): {}", file_name, mode, stderr); assert!( !stderr.contains("E9001") && !stderr.contains("compiler internal error"), "{} ({}) leaked a backend failure: {}", @@ -2757,23 +2487,11 @@ fun main() { assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("duplicate_type.wave:2:8"), "{}", stderr); - assert!( - stderr.contains("struct Item { second: i32; }"), - "{}", - stderr - ); + assert!(stderr.contains("struct Item { second: i32; }"), "{}", stderr); let imported = dir.join("broken.wave"); - fs::write( - &imported, - "fun broken() {\n var value: Missing = 1;\n}\n", - ) - .unwrap(); - let entry = write_wave( - &dir, - "import_main.wave", - "import(\"./broken\");\n\nfun main() {}\n", - ); + fs::write(&imported, "fun broken() {\n var value: Missing = 1;\n}\n").unwrap(); + let entry = write_wave(&dir, "import_main.wave", "import(\"./broken\");\n\nfun main() {}\n"); let output = run_wavec_raw([OsStr::new("check"), entry.as_os_str()]); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); @@ -2796,16 +2514,8 @@ fun main() { assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("generic_broken.wave:3:9"), "{}", stderr); - assert!( - stderr.contains("type `Box` expects 1 generic argument(s), found 2"), - "{}", - stderr - ); - assert!( - !stderr.contains("generic monomorphization failed"), - "{}", - stderr - ); + assert!(stderr.contains("type `Box` expects 1 generic argument(s), found 2"), "{}", stderr); + assert!(!stderr.contains("generic monomorphization failed"), "{}", stderr); } #[test] @@ -2893,11 +2603,7 @@ fn vex_cli_print_json_contracts_are_machine_readable() { assert!(stderr.trim().is_empty(), "unexpected stderr:\n{}", stderr); let json = stdout.trim(); assert!(json.starts_with('{') && json.ends_with('}'), "{}", json); - assert!( - json.contains("\"triple\":\"x86_64-unknown-linux-gnu\""), - "{}", - json - ); + assert!(json.contains("\"triple\":\"x86_64-unknown-linux-gnu\""), "{}", json); assert!(json.contains("\"arch\":\"x86_64\""), "{}", json); assert!(json.contains("\"os\":\"linux\""), "{}", json); assert!(json.contains("\"env\":\"gnu\""), "{}", json); @@ -3000,22 +2706,14 @@ fun main() -> i32 { out_dir.as_os_str(), ]); assert!(stderr.trim().is_empty(), "unexpected stderr:\n{stderr}"); - assert!( - plan.contains("\"target\":\"wasm32-unknown-unknown\""), - "{plan}" - ); - assert!( - plan.contains("\"program\":") && plan.contains("wasm-ld"), - "{plan}" - ); + assert!(plan.contains("\"target\":\"wasm32-unknown-unknown\""), "{plan}"); + assert!(plan.contains("\"program\":") && plan.contains("wasm-ld"), "{plan}"); assert!(plan.contains("--no-entry"), "{plan}"); assert!(plan.contains("--allow-undefined"), "{plan}"); assert!(plan.contains("--export-if-defined=main"), "{plan}"); assert!(plan.contains("--export-memory"), "{plan}"); assert!( - plan.contains(&json_string_for_test( - &out_dir.join("module.wasm").to_string_lossy() - )), + plan.contains(&json_string_for_test(&out_dir.join("module.wasm").to_string_lossy())), "{plan}" ); @@ -3069,11 +2767,8 @@ fun main() -> i32 { "memory64 host flags must be selected at execution time: {wasm64_plan}" ); - let asm_source = write_wave( - &dir, - "inline_asm.wave", - "fun main() {\n asm {\n \"nop\"\n }\n}\n", - ); + let asm_source = + write_wave(&dir, "inline_asm.wave", "fun main() {\n asm {\n \"nop\"\n }\n}\n"); let asm_error = run_wavec_expect_failure([ OsStr::new("build"), asm_source.as_os_str(), @@ -3093,10 +2788,7 @@ fun main() -> i32 { #[cfg(feature = "llvm-target-wasm")] fn wasm_host_output_preserves_bytes_numeric_formatting_and_exit_status() { for tool in ["node", "wasm-ld"] { - if !Command::new(tool) - .arg("--version") - .output() - .is_ok_and(|output| output.status.success()) + if !Command::new(tool).arg("--version").output().is_ok_and(|output| output.status.success()) { eprintln!("skipping WebAssembly output execution: {tool} unavailable"); return; @@ -3129,11 +2821,7 @@ fun main() -> i32 { "#, ); let expected = "한글 text A -42 340282366920938463463374607431768211455 -0.000000 0.007812 100% host puts\n"; - for target in [ - "wasm32-unknown-unknown", - "wasm32-wasip1", - "wasm64-unknown-unknown", - ] { + for target in ["wasm32-unknown-unknown", "wasm32-wasip1", "wasm64-unknown-unknown"] { for optimization in ["-O0", "-O2"] { let output = wavec_command() .arg("build") @@ -3150,11 +2838,7 @@ fun main() -> i32 { "{target} {optimization}: {}", String::from_utf8_lossy(&output.stderr) ); - assert_eq!( - output.stdout, - expected.as_bytes(), - "{target} {optimization}" - ); + assert_eq!(output.stdout, expected.as_bytes(), "{target} {optimization}"); } } fs::remove_dir_all(dir).unwrap(); @@ -3164,10 +2848,7 @@ fun main() -> i32 { #[cfg(feature = "llvm-target-wasm")] fn wasi_runner_preserves_explicit_process_exit_status() { for tool in ["node", "wasm-ld"] { - if !Command::new(tool) - .arg("--version") - .output() - .is_ok_and(|output| output.status.success()) + if !Command::new(tool).arg("--version").output().is_ok_and(|output| output.status.success()) { eprintln!("skipping WASI execution: {tool} unavailable"); return; @@ -3243,10 +2924,7 @@ fun main() -> i32 { return 0; } && host_ir.contains("\"wasm-import-name\"=\"host_transform\""), "{host_ir}" ); - assert!( - host_ir.contains("\"wasm-export-name\"=\"wave_transform\""), - "{host_ir}" - ); + assert!(host_ir.contains("\"wasm-export-name\"=\"wave_transform\""), "{host_ir}"); let host64_out = dir.join("host64-out"); run_wavec([ @@ -3287,10 +2965,7 @@ fun main() -> i32 { return 0; } wasi_out.as_os_str(), ]); let wasi_ir = fs::read_to_string(wasi_out.join("wasi.ll")).unwrap(); - assert!( - wasi_ir.contains("target triple = \"wasm32-wasip1\""), - "{wasi_ir}" - ); + assert!(wasi_ir.contains("target triple = \"wasm32-wasip1\""), "{wasi_ir}"); assert!( wasi_ir.contains("\"wasm-import-module\"=\"wasi_snapshot_preview1\"") && wasi_ir.contains("\"wasm-import-name\"=\"fd_write\""), @@ -3370,11 +3045,7 @@ fun main() -> i32 { let plan = stdout.trim(); assert!(plan.starts_with('{') && plan.ends_with('}'), "{}", plan); assert!(plan.contains("\"schema_version\":1"), "{}", plan); - assert!( - plan.contains("\"target\":\"x86_64-unknown-none-elf\""), - "{}", - plan - ); + assert!(plan.contains("\"target\":\"x86_64-unknown-none-elf\""), "{}", plan); assert!(plan.contains("\"cpu\":\"generic\""), "{}", plan); assert!(plan.contains("\"features\":\"\""), "{}", plan); assert!(plan.contains("\"abi\":null"), "{}", plan); @@ -3468,16 +3139,9 @@ fun main() -> i32 { assert!(plan.contains("\"kind\":\"wave\""), "{}", plan); assert!(plan.contains("\"kind\":\"obj\""), "{}", plan); assert!(plan.contains("\"kind\":\"archive\""), "{}", plan); + assert!(plan.contains("\"emit_kinds\":[\"obj\",\"bin\"]"), "{}", plan); assert!( - plan.contains("\"emit_kinds\":[\"obj\",\"bin\"]"), - "{}", - plan - ); - assert!( - plan.contains(&format!( - "\"output\":{}", - json_string_for_test(&binary.to_string_lossy()) - )), + plan.contains(&format!("\"output\":{}", json_string_for_test(&binary.to_string_lossy()))), "-o must apply to final linked binary only:\n{}", plan ); @@ -3514,11 +3178,7 @@ fun main() -> i32 { let stdout = String::from_utf8_lossy(&run_plan.stdout); assert!(stdout.contains("\"mode\":\"build+run\""), "{}", stdout); assert!(stdout.contains("\"execute\":{"), "{}", stdout); - assert!( - stdout.contains("\"args\":[\"arg one\",\"--flag\"]"), - "{}", - stdout - ); + assert!(stdout.contains("\"args\":[\"arg one\",\"--flag\"]"), "{}", stdout); let cases: &[(&str, &[&str], &[&str])] = &[ ( @@ -3543,14 +3203,7 @@ fun main() -> i32 { ), ( "no-pie relocation conflict", - &[ - "-C", - "relocation-model=pie", - "build", - "main.wave", - "--no-pie", - "--dry-run", - ], + &["-C", "relocation-model=pie", "build", "main.wave", "--no-pie", "--dry-run"], &["--no-pie", "relocation-model=pie"], ), ( @@ -3566,9 +3219,8 @@ fun main() -> i32 { ]; for (name, args, needles) in cases { - let full_args = std::iter::once("--error-format=json") - .chain(args.iter().copied()) - .map(OsStr::new); + let full_args = + std::iter::once("--error-format=json").chain(args.iter().copied()).map(OsStr::new); let out = run_wavec_raw(full_args); assert!( !out.status.success(), @@ -3579,20 +3231,9 @@ fun main() -> i32 { ); assert_eq!(out.status.code(), Some(2), "{}", name); let stderr = String::from_utf8_lossy(&out.stderr); - assert!( - stderr.contains("\"kind\":\"usage\""), - "{}: {}", - name, - stderr - ); + assert!(stderr.contains("\"kind\":\"usage\""), "{}: {}", name, stderr); for needle in *needles { - assert!( - stderr.contains(needle), - "{} missing '{}': {}", - name, - needle, - stderr - ); + assert!(stderr.contains(needle), "{} missing '{}': {}", name, needle, stderr); } } } @@ -3690,10 +3331,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { String::from_utf8_lossy(&output.stderr) ); let stdout = String::from_utf8_lossy(&output.stdout); - assert!( - stdout.trim().is_empty(), - "JSON usage errors must not write to stdout: {stdout}" - ); + assert!(stdout.trim().is_empty(), "JSON usage errors must not write to stdout: {stdout}"); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("\"kind\":\"usage\""), "{}", stderr); assert!(stderr.contains(expected), "{}", stderr); @@ -3709,12 +3347,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { for mode in [ &["--target", "mips64-unknown-linux-gnu", "--emit=check"][..], &["--target", "mips64-unknown-linux-gnu", "--emit=obj"][..], - &[ - "--target", - "mips64-unknown-linux-gnu", - "--emit=obj", - "--dry-run", - ][..], + &["--target", "mips64-unknown-linux-gnu", "--emit=obj", "--dry-run"][..], ] { run_failure(mode, "unsupported target 'mips64-unknown-linux-gnu'"); } @@ -3729,83 +3362,37 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { "unsupported target 'riscv64-unknown-linux-musl'", ), ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--cpu", - "sifive-u74", - "--emit=check", - ][..], + &["--target", "x86_64-unknown-linux-gnu", "--cpu", "sifive-u74", "--emit=check"][..], "unsupported CPU 'sifive-u74'", ), - ( - &[ - "--target", - "riscv64-unknown-none-elf", - "--cpu", - "rocket", - "--emit=obj", - ][..], - "unsupported CPU 'rocket'", - ), - ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--features", - "+m", - "--emit=check", - ][..], + ( + &["--target", "riscv64-unknown-none-elf", "--cpu", "rocket", "--emit=obj"][..], + "unsupported CPU 'rocket'", + ), + ( + &["--target", "x86_64-unknown-linux-gnu", "--features", "+m", "--emit=check"][..], "unsupported feature 'm'", ), ( - &[ - "--target", - "aarch64-unknown-linux-gnu", - "--features", - "+fp", - "--emit=obj", - ][..], + &["--target", "aarch64-unknown-linux-gnu", "--features", "+fp", "--emit=obj"][..], "unsupported feature 'fp'", ), ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--abi", - "lp64d", - "--emit=check", - ][..], + &["--target", "x86_64-unknown-linux-gnu", "--abi", "lp64d", "--emit=check"][..], "unsupported ABI 'lp64d'", ), ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--features", - "sse2", - "--emit=check", - ][..], + &["--target", "x86_64-unknown-linux-gnu", "--features", "sse2", "--emit=check"][..], "invalid target feature 'sse2'", ), ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--features", - "+sse2,-sse2", - "--emit=check", - ][..], + &["--target", "x86_64-unknown-linux-gnu", "--features", "+sse2,-sse2", "--emit=check"] + [..], "target feature 'sse2' is specified more than once", ), ( - &[ - "--target", - "x86_64-unknown-linux-gnu", - "--features", - "+sse2,,+avx", - "--emit=check", - ][..], + &["--target", "x86_64-unknown-linux-gnu", "--features", "+sse2,,+avx", "--emit=check"] + [..], "invalid empty target feature", ), ( @@ -3845,13 +3432,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { "ABI 'lp64f' for target 'riscv64-unknown-none-elf' requires feature 'f'", ), ( - &[ - "--target", - "riscv64-unknown-linux-gnu", - "--features", - "-zicsr", - "--emit=check", - ][..], + &["--target", "riscv64-unknown-linux-gnu", "--features", "-zicsr", "--emit=check"][..], "feature 'f' requires feature 'zicsr'", ), ] { @@ -3920,11 +3501,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { assert!(stdout.contains("\"hosted\":false"), "{}", stdout); assert!(stdout.contains("\"freestanding\":true"), "{}", stdout); assert!(stdout.contains("\"cpu\":\"generic-rv64\""), "{}", stdout); - assert!( - stdout.contains("\"features\":\"+m,+a,-f,-d,+c,-zicsr,-zifencei\""), - "{}", - stdout - ); + assert!(stdout.contains("\"features\":\"+m,+a,-f,-d,+c,-zicsr,-zifencei\""), "{}", stdout); assert!(stdout.contains("\"abi\":\"lp64\""), "{}", stdout); assert!(stdout.contains("\"isa\":\"rv64imac\""), "{}", stdout); @@ -3938,11 +3515,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { assert!(stderr.trim().is_empty(), "{}", stderr); assert!(stdout.contains("\"hosted\":true"), "{}", stdout); assert!(stdout.contains("\"freestanding\":false"), "{}", stdout); - assert!( - stdout.contains("\"features\":\"+m,+a,+f,+d,+c,+zicsr,+zifencei\""), - "{}", - stdout - ); + assert!(stdout.contains("\"features\":\"+m,+a,+f,+d,+c,+zicsr,+zifencei\""), "{}", stdout); assert!(stdout.contains("\"abi\":\"lp64d\""), "{}", stdout); assert!(stdout.contains("\"isa\":\"rv64gc\""), "{}", stdout); @@ -3957,11 +3530,7 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { ]); assert!(stderr.trim().is_empty(), "{}", stderr); assert!(stdout.contains("\"cpu\":\"generic-rv64\""), "{}", stdout); - assert!( - stdout.contains("\"features\":\"+m,+a,+f,+d,+c,+zicsr,+zifencei\""), - "{}", - stdout - ); + assert!(stdout.contains("\"features\":\"+m,+a,+f,+d,+c,+zicsr,+zifencei\""), "{}", stdout); assert!(stdout.contains("\"abi\":\"lp64d\""), "{}", stdout); assert!(stdout.contains("\"isa\":\"rv64gc\""), "{}", stdout); @@ -3976,16 +3545,8 @@ fn target_configuration_is_rejected_before_frontend_or_backend_work() { OsStr::new("--dry-run"), ]); assert!(stderr.trim().is_empty(), "{}", stderr); - assert!( - stdout.contains("--dynamic-linker=/lib/ld-linux-riscv64-lp64.so.1"), - "{}", - stdout - ); - assert!( - !stdout.contains("ld-linux-riscv64-lp64d.so.1"), - "{}", - stdout - ); + assert!(stdout.contains("--dynamic-linker=/lib/ld-linux-riscv64-lp64.so.1"), "{}", stdout); + assert!(!stdout.contains("ld-linux-riscv64-lp64d.so.1"), "{}", stdout); assert!( json_contains_path_components( &stdout, @@ -4085,20 +3646,14 @@ fn freebsd_link_plans_use_target_sysroot_and_elf_loader() { ]); assert!(stderr.is_empty(), "{stderr}"); assert!(plan.contains(&format!("\"-m\",\"{emulation}\"")), "{plan}"); - assert!( - plan.contains("--dynamic-linker=/libexec/ld-elf.so.1"), - "{plan}" - ); + assert!(plan.contains("--dynamic-linker=/libexec/ld-elf.so.1"), "{plan}"); for file in [start, "crti.o", "crtn.o"] { assert!( json_contains_path_components(&plan, &["sysroot", "usr", "lib", file]), "{plan}" ); } - assert!( - !plan.contains("ld-linux") && !plan.contains("unknown-linux-gnu"), - "{plan}" - ); + assert!(!plan.contains("ld-linux") && !plan.contains("unknown-linux-gnu"), "{plan}"); } } } @@ -4192,17 +3747,10 @@ fn riscv64_debian_cross_prefix_does_not_double_apply_linker_sysroot() { ), ) .unwrap(); - fs::write( - runtime.join("libm.so"), - format!("GROUP ( {runtime_prefix}/libm.so.6 )\n"), - ) - .unwrap(); - for runtime_file in [ - "libc.so.6", - "libc_nonshared.a", - "libm.so.6", - "ld-linux-riscv64-lp64d.so.1", - ] { + fs::write(runtime.join("libm.so"), format!("GROUP ( {runtime_prefix}/libm.so.6 )\n")).unwrap(); + for runtime_file in + ["libc.so.6", "libc_nonshared.a", "libm.so.6", "ld-linux-riscv64-lp64d.so.1"] + { fs::write(runtime.join(runtime_file), []).unwrap(); } @@ -4216,16 +3764,8 @@ fn riscv64_debian_cross_prefix_does_not_double_apply_linker_sysroot() { OsStr::new("--format=json"), ]); assert!(stderr.trim().is_empty(), "{}", stderr); - assert!( - target_spec.contains("\"sysroot_source\":\"explicit\""), - "{}", - target_spec - ); - assert!( - json_contains_path_value(&target_spec, &sysroot.to_string_lossy()), - "{}", - target_spec - ); + assert!(target_spec.contains("\"sysroot_source\":\"explicit\""), "{}", target_spec); + assert!(json_contains_path_value(&target_spec, &sysroot.to_string_lossy()), "{}", target_spec); let (stdout, stderr) = run_wavec_capture([ OsStr::new("--error-format=json"), @@ -4239,11 +3779,7 @@ fn riscv64_debian_cross_prefix_does_not_double_apply_linker_sysroot() { OsStr::new("--dry-run"), ]); assert!(stderr.trim().is_empty(), "{}", stderr); - assert!( - stdout.contains("\"sysroot_source\":\"explicit\""), - "{}", - stdout - ); + assert!(stdout.contains("\"sysroot_source\":\"explicit\""), "{}", stdout); assert!(stdout.contains("--sysroot=/"), "{}", stdout); assert!( !stdout.contains(&format!("--sysroot={}", sysroot.display())), @@ -4281,9 +3817,7 @@ fn advertised_target_options_reach_object_codegen_without_backend_diagnostics() assert!(stdout.trim().is_empty(), "{}", stdout); let only_nonstandard_lp64f_warnings = target.starts_with("loongarch64-") && !stderr.trim().is_empty() - && stderr - .lines() - .all(|line| line == "warning: 'lp64f' has not been standardized"); + && stderr.lines().all(|line| line == "warning: 'lp64f' has not been standardized"); assert!( stderr.trim().is_empty() || only_nonstandard_lp64f_warnings, "advertised target option emitted a backend diagnostic for {target}: {stderr}" @@ -4312,10 +3846,7 @@ fn advertised_target_options_reach_object_codegen_without_backend_diagnostics() if target_spec.contains("\"object_format\":\"elf\"") { assert!(object.starts_with(b"\x7fELF"), "{target}: {target_spec}"); } else if target_spec.contains("\"object_format\":\"macho\"") { - assert!( - object.starts_with(&[0xcf, 0xfa, 0xed, 0xfe]), - "{target}: {target_spec}" - ); + assert!(object.starts_with(&[0xcf, 0xfa, 0xed, 0xfe]), "{target}: {target_spec}"); } else if target_spec.contains("\"object_format\":\"coff\"") { let machine = if target_spec.contains("\"arch\":\"aarch64\"") { [0x64, 0xaa] @@ -4362,7 +3893,7 @@ fn advertised_target_options_reach_object_codegen_without_backend_diagnostics() (true, "d", "+") => "+f,+d".to_string(), _ if target.starts_with("riscv64-") && feature == "zicsr" && sign == "-" => { "-f,-d,-zicsr".to_string() - } + }, _ => format!("{sign}{feature}"), }; if target == "riscv64-unknown-freebsd" @@ -4553,11 +4084,7 @@ fn projected_deref_preserves_pointer_storage_types() { // Use the advertised targets so this regression also runs in isolated // backend builds and covers WebAssembly's 32-bit pointer representation. let (targets, _) = run_wavec_capture(["print", "target-list"]); - for target in targets - .lines() - .map(str::trim) - .filter(|line| !line.is_empty()) - { + for target in targets.lines().map(str::trim).filter(|line| !line.is_empty()) { let out = dir.join(target); run_wavec([ OsStr::new("build"), @@ -4570,24 +4097,12 @@ fn projected_deref_preserves_pointer_storage_types() { ]); let ir = fs::read_to_string(out.join("test103.ll")).unwrap(); for name in ["check_writer", "check_index", "check_depth"] { - let body = ir - .split(&format!("@{name}(")) - .nth(1) - .unwrap() - .split("\n}") - .next() - .unwrap(); + let body = ir.split(&format!("@{name}(")).nth(1).unwrap().split("\n}").next().unwrap(); assert!(body.contains("load ptr"), "{target} {name}: {body}"); assert!(!body.contains("load i8"), "{target} {name}: {body}"); } for name in ["widen_field", "widen_scalar"] { - let body = ir - .split(&format!("@{name}(")) - .nth(1) - .unwrap() - .split("\n}") - .next() - .unwrap(); + let body = ir.split(&format!("@{name}(")).nth(1).unwrap().split("\n}").next().unwrap(); assert!(body.contains("load i8"), "{target} {name}: {body}"); assert!(!body.contains("load i64"), "{target} {name}: {body}"); } @@ -5066,11 +4581,7 @@ fn riscv64_ir_and_bitcode_preserve_target_contract_when_recompiled() { assert!(ir.contains("!\"target-abi\", !\"lp64f\""), "{}", ir); assert!(ir.contains("!\"riscv-isa\""), "{}", ir); assert!(ir.contains("\"target-cpu\"=\"generic-rv64\""), "{}", ir); - assert!( - ir.contains("\"target-features\"=\"+m,+a,+f,-d,+c,+zicsr,+zifencei\""), - "{}", - ir - ); + assert!(ir.contains("\"target-features\"=\"+m,+a,+f,-d,+c,+zicsr,+zifencei\""), "{}", ir); let from_ir = dir.join("from-ir"); let ir_input = original.join("main.ll"); @@ -5091,17 +4602,8 @@ fn riscv64_ir_and_bitcode_preserve_target_contract_when_recompiled() { from_bc.as_os_str(), ]); - for object in [ - original.join("main.o"), - from_ir.join("main.o"), - from_bc.join("main.o"), - ] { - assert_eq!( - riscv64_elf_flags(&object) & 0x7, - 0x3, - "{}", - object.display() - ); + for object in [original.join("main.o"), from_ir.join("main.o"), from_bc.join("main.o")] { + assert_eq!(riscv64_elf_flags(&object) & 0x7, 0x3, "{}", object.display()); let bytes = fs::read(&object).unwrap(); assert!(bytes_contains(&bytes, b"zicsr"), "{}", object.display()); assert!(bytes_contains(&bytes, b"zifencei"), "{}", object.display()); @@ -5155,11 +4657,7 @@ fun main() -> i32 { "{}", ir ); - assert!( - ir.contains("define void @wave_make(ptr sret(%Triple) align 8"), - "{}", - ir - ); + assert!(ir.contains("define void @wave_make(ptr sret(%Triple) align 8"), "{}", ir); assert!(ir.contains("@__wave_export_impl_wave_take"), "{}", ir); assert!(ir.contains("@__wave_export_impl_wave_make"), "{}", ir); } @@ -5350,17 +4848,14 @@ fn odd_sized_aggregate_transport_matches_clang_ir_contracts() { ("x86_64", true) => (object_integer.clone(), object_integer), ("x86_64", false) => { let remainder = (size - 8) * 8; - ( - format!("{{ i64, i{remainder} }}"), - format!("i64, i{remainder}"), - ) - } + (format!("{{ i64, i{remainder} }}"), format!("i64, i{remainder}")) + }, ("aarch64", true) => (object_integer, "i64".to_string()), ("aarch64", false) => ("[2 x i64]".to_string(), "[2 x i64]".to_string()), ("riscv64" | "loongarch64", true) => ("i64".to_string(), "i64".to_string()), ("riscv64" | "loongarch64", false) => { ("[2 x i64]".to_string(), "[2 x i64]".to_string()) - } + }, _ => unreachable!(), }; let c_contract = format!("{result} @c_bytes{size}({argument}"); @@ -5375,14 +4870,8 @@ fn odd_sized_aggregate_transport_matches_clang_ir_contracts() { clang_c_definition.contains(&c_contract), "missing `{c_contract}` in `{clang_c_definition}`" ); - assert!( - wave_ir.contains(&format!("declare {c_contract}")), - "{wave_ir}" - ); - assert!( - clang_ir.contains(&wave_contract), - "missing `{wave_contract}`:\n{clang_ir}" - ); + assert!(wave_ir.contains(&format!("declare {c_contract}")), "{wave_ir}"); + assert!(clang_ir.contains(&wave_contract), "missing `{wave_contract}`:\n{clang_ir}"); let wave_definition = wave_ir .lines() .find(|line| line.contains(&format!("@wave_bytes{size}("))) @@ -5395,10 +4884,7 @@ fn odd_sized_aggregate_transport_matches_clang_ir_contracts() { ); } for contract in ["void @c_empty()", "void @wave_empty()"] { - assert!( - clang_ir.contains(contract), - "missing `{contract}`:\n{clang_ir}" - ); + assert!(clang_ir.contains(contract), "missing `{contract}`:\n{clang_ir}"); } assert!(wave_ir.contains("declare void @c_empty()"), "{wave_ir}"); assert!(wave_ir.contains("define void @wave_empty()"), "{wave_ir}"); @@ -5437,23 +4923,16 @@ fn odd_sized_aggregate_transport_matches_clang_ir_contracts() { .unwrap_or_else(|| panic!("missing c_{name} definition:\n{clang_ir}")) .replace(" %0", ""); assert!( - clang_contracts - .iter() - .any(|contract| clang_definition.contains(contract)), + clang_contracts.iter().any(|contract| clang_definition.contains(contract)), "missing one of {clang_contracts:?} for {target} in `{clang_definition}`" ); - assert!( - wave_ir.contains(&format!("declare {c_contract}")), - "{wave_ir}" - ); + assert!(wave_ir.contains(&format!("declare {c_contract}")), "{wave_ir}"); let mut clang_wave_contracts = vec![wave_contract.clone()]; if matches!(tag, "x86_64" | "aarch64") && *name == "pointer_member" { clang_wave_contracts.push("i64 @wave_pointer_member(i64".to_string()); } assert!( - clang_wave_contracts - .iter() - .any(|contract| clang_ir.contains(contract)), + clang_wave_contracts.iter().any(|contract| clang_ir.contains(contract)), "missing one of {clang_wave_contracts:?} for {target}:\n{clang_ir}" ); let wave_definition = wave_ir @@ -5635,10 +5114,7 @@ fun main() -> i32 { "extern(c) fun consume(count: i32, ...) -> i64; fun main() { consume(1, null); }\n", ); let error = run_wavec_expect_failure([OsStr::new("check"), invalid.as_os_str()]); - assert!( - error.contains("variadic argument 2") && error.contains("no scalar type"), - "{error}" - ); + assert!(error.contains("variadic argument 2") && error.contains("no scalar type"), "{error}"); } #[test] @@ -5726,14 +5202,8 @@ fn riscv_link_input_abi_is_validated_before_linking() { validate_riscv_link_inputs(RiscvFloatAbi::Lp64d, &[archive.display().to_string()]) .unwrap_err() .to_string(); - assert!( - archive_error.contains("libmixed.a(main.o)"), - "{archive_error}" - ); - assert!( - archive_error.contains("input ABI: LP64F"), - "{archive_error}" - ); + assert!(archive_error.contains("libmixed.a(main.o)"), "{archive_error}"); + assert!(archive_error.contains("input ABI: LP64F"), "{archive_error}"); for (input, expected_input_abi) in [(&objects[0].1, "LP64"), (&archive, "LP64F")] { let output = run_wavec_raw([ @@ -5751,15 +5221,9 @@ fn riscv_link_input_abi_is_validated_before_linking() { ]); assert!(!output.status.success()); let error = String::from_utf8_lossy(&output.stderr); - assert!( - error.contains("RISC-V floating-point ABI mismatch before linking"), - "{error}" - ); + assert!(error.contains("RISC-V floating-point ABI mismatch before linking"), "{error}"); assert!(error.contains("target ABI: LP64D"), "{error}"); - assert!( - error.contains(&format!("input ABI: {expected_input_abi}")), - "{error}" - ); + assert!(error.contains(&format!("input ABI: {expected_input_abi}")), "{error}"); assert!( !error.contains("link failed"), "external linker ran before ABI validation: {error}" @@ -5782,11 +5246,8 @@ fn loongarch64_link_inputs_require_matching_abi_before_linking() { out.as_os_str(), ]); let compatible = out.join("main.o"); - validate_loongarch64_link_inputs( - LoongArchFloatAbi::Lp64d, - &[compatible.display().to_string()], - ) - .unwrap(); + validate_loongarch64_link_inputs(LoongArchFloatAbi::Lp64d, &[compatible.display().to_string()]) + .unwrap(); let incompatible = dir.join("soft-float.o"); let mut bytes = fs::read(&compatible).unwrap(); @@ -5814,14 +5275,8 @@ fn loongarch64_link_inputs_require_matching_abi_before_linking() { ]); assert!(!output.status.success()); let error = String::from_utf8_lossy(&output.stderr); - assert!( - error.contains("LoongArch floating-point ABI mismatch before linking"), - "{error}" - ); - assert!( - !error.contains("link failed"), - "external linker ran before ABI validation: {error}" - ); + assert!(error.contains("LoongArch floating-point ABI mismatch before linking"), "{error}"); + assert!(!error.contains("link failed"), "external linker ran before ABI validation: {error}"); } #[cfg(any(feature = "llvm-target-all", feature = "llvm-target-loongarch"))] @@ -5841,10 +5296,7 @@ fn loongarch64_lp64f_hosted_linking_is_rejected_before_the_linker() { assert!(!output.status.success()); let error = String::from_utf8_lossy(&output.stderr); - assert!( - error.contains("glibc does not provide an LP64F runtime"), - "{error}" - ); + assert!(error.contains("glibc does not provide an LP64F runtime"), "{error}"); assert!( !error.contains("link failed"), "external linker ran before LP64F runtime validation: {error}" @@ -5918,10 +5370,7 @@ fun main() -> i32 { return 0; } ]); let ir = fs::read_to_string(output.join("modes.ll")).unwrap(); for declaration in declarations { - assert!( - ir.contains(declaration), - "{abi}: missing `{declaration}`\n{ir}" - ); + assert!(ir.contains(declaration), "{abi}: missing `{declaration}`\n{ir}"); } let object = output.join("modes.o"); assert_eq!(loongarch64_elf_flags(&object) & 0x7, expected_flags); @@ -5965,22 +5414,14 @@ fn run_linux_c_abi_fixture_at_optimization( optimization: &str, ) { let dir = temp_case_dir(&format!("{fixture_name}-c-abi-interop")); - let fixture_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("fixtures") - .join(fixture_name); + let fixture_dir = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("fixtures").join(fixture_name); let c_object = dir.join("interop-c.o"); let wave_out = dir.join("wave"); let binary = dir.join("interop"); let c_compile = Command::new(c_compiler) - .args([ - "-O2", - "-ffreestanding", - "-fno-builtin", - "-fno-stack-protector", - "-c", - ]) + .args(["-O2", "-ffreestanding", "-fno-builtin", "-fno-stack-protector", "-c"]) .arg(fixture_dir.join("interop.c")) .arg("-o") .arg(&c_object) @@ -6038,10 +5479,7 @@ fn run_linux_c_abi_fixture_at_optimization( fn pe_machine(path: &Path) -> u16 { let bytes = fs::read(path).unwrap(); - assert!( - bytes.len() >= 0x40 && &bytes[..2] == b"MZ", - "invalid PE file" - ); + assert!(bytes.len() >= 0x40 && &bytes[..2] == b"MZ", "invalid PE file"); let pe_offset = u32::from_le_bytes(bytes[0x3c..0x40].try_into().unwrap()) as usize; assert!( bytes.len() >= pe_offset + 6 && &bytes[pe_offset..pe_offset + 4] == b"PE\0\0", @@ -6073,11 +5511,7 @@ fn windows_arm64_c_abi_emits_msvc_objects() { .arg(&c_object) .output() .unwrap(); - assert!( - compiled.status.success(), - "{}", - String::from_utf8_lossy(&compiled.stderr) - ); + assert!(compiled.status.success(), "{}", String::from_utf8_lossy(&compiled.stderr)); run_wavec([ OsStr::new("build"), fixture_dir.join("interop.wave").as_os_str(), @@ -6126,21 +5560,13 @@ fn aarch64_c_abi_interoperates_with_c() { run_linux_c_abi_fixture( "aarch64_aapcs64", "aarch64-unknown-linux-gnu", - if native { - "gcc" - } else { - "aarch64-linux-gnu-gcc" - }, + if native { "gcc" } else { "aarch64-linux-gnu-gcc" }, if native { None } else { Some("qemu-aarch64") }, ); run_linux_c_abi_fixture( "c_abi_edges", "aarch64-unknown-linux-gnu", - if native { - "gcc" - } else { - "aarch64-linux-gnu-gcc" - }, + if native { "gcc" } else { "aarch64-linux-gnu-gcc" }, if native { None } else { Some("qemu-aarch64") }, ); } @@ -6195,13 +5621,7 @@ fn riscv64_c_abi_interoperates_with_c_under_qemu() { ]); let link = Command::new("riscv64-linux-gnu-gcc") - .args([ - "-march=rv64gc", - "-mabi=lp64d", - "-nostdlib", - "-static", - "-Wl,-e,_start", - ]) + .args(["-march=rv64gc", "-mabi=lp64d", "-nostdlib", "-static", "-Wl,-e,_start"]) .arg(wave_out.join("interop.o")) .arg(&c_object) .arg("-o") @@ -6214,10 +5634,8 @@ fn riscv64_c_abi_interoperates_with_c_under_qemu() { String::from_utf8_lossy(&link.stderr) ); - let run = Command::new("qemu-riscv64") - .arg(&binary) - .output() - .expect("failed to start qemu-riscv64"); + let run = + Command::new("qemu-riscv64").arg(&binary).output().expect("failed to start qemu-riscv64"); assert!( run.status.success(), "C/Wave psABI fixture failed with status {}\nstdout:\n{}\nstderr:\n{}", @@ -6259,16 +5677,12 @@ fn run_loongarch64_c_abi_fixture(abi: &str, fixture_name: &str) { .find(|tool| Command::new(tool).arg("--version").output().is_ok()) .expect("ld.lld 21 is required for LoongArch ABI tests"); assert!( - Command::new("qemu-loongarch64") - .arg("--version") - .output() - .is_ok(), + Command::new("qemu-loongarch64").arg("--version").output().is_ok(), "qemu-loongarch64 is required for LoongArch ABI tests" ); - let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures") - .join(fixture_name); + let fixture = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(fixture_name); let dir = temp_case_dir(&format!("loongarch64-{abi}-c-abi-interop")); let c_object = dir.join("interop-c.o"); let wave_out = dir.join("wave"); @@ -6324,10 +5738,7 @@ fn run_loongarch64_c_abi_fixture(abi: &str, fixture_name: &str) { ] }; for contract in contracts { - assert!( - wave_ir.contains(*contract), - "missing `{contract}`:\n{wave_ir}" - ); + assert!(wave_ir.contains(*contract), "missing `{contract}`:\n{wave_ir}"); } let expected_flags = if abi == "lp64s" { 1 } else { 3 }; @@ -6691,11 +6102,7 @@ fn run_shared_language_workloads(flag: &str, target: &str, runner: &str) { .arg(&runtime) .output() .unwrap(); - assert!( - compile.status.success(), - "{}", - String::from_utf8_lossy(&compile.stderr) - ); + assert!(compile.status.success(), "{}", String::from_utf8_lossy(&compile.stderr)); let arch = match target { "aarch64-unknown-linux-gnu" => "arm64", "riscv64-unknown-linux-gnu" => "riscv64", @@ -6732,14 +6139,7 @@ fn run_shared_language_workloads(flag: &str, target: &str, runner: &str) { ]); let binary = output_dir.join(case); let link = Command::new(&clang) - .args([ - "-target", - target, - "-fuse-ld=lld", - "-nostdlib", - "-static", - "-Wl,-e,_start", - ]) + .args(["-target", target, "-fuse-ld=lld", "-nostdlib", "-static", "-Wl,-e,_start"]) .arg(&runtime) .arg(output_dir.join(format!("{case}.o"))) .arg("-o") @@ -6826,42 +6226,23 @@ fn msvc_link_companions_keep_final_names_and_survive_failed_replacement() { )); } let output = command.output().unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); for extension in ["dll", "lib", "pdb"] { assert!( output_dir.join(format!("answer.{extension}")).is_file(), "missing final {extension} for {target}" ); } - assert_eq!( - pe_machine(&dll), - if target.starts_with("aarch64-") { - 0xaa64 - } else { - 0x8664 - } - ); + assert_eq!(pe_machine(&dll), if target.starts_with("aarch64-") { 0xaa64 } else { 0x8664 }); let library = fs::read(output_dir.join("answer.lib")).unwrap(); - assert!(library - .windows(b"answer.dll".len()) - .any(|bytes| bytes == b"answer.dll")); - assert!(!library - .windows(b".wave-output-".len()) - .any(|bytes| bytes == b".wave-output-")); + assert!(library.windows(b"answer.dll".len()).any(|bytes| bytes == b"answer.dll")); + assert!(!library.windows(b".wave-output-".len()).any(|bytes| bytes == b".wave-output-")); let custom_lib = output_dir.join("imports").join("public.lib"); let custom_pdb = output_dir.join("symbols").join("private.pdb"); command.arg(format!("-Clink-arg=/IMPLIB:{}", custom_lib.display())); command.arg(format!("-Clink-arg=/PDB:{}", custom_pdb.display())); let output = command.output().unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); assert_eq!(fs::read(&custom_lib).unwrap(), library); assert!(custom_pdb.is_file()); let consumer = write_wave( @@ -6887,9 +6268,7 @@ fun main() -> i32 { return 0; } executable.as_os_str(), ]); let image = fs::read(&executable).unwrap(); - assert!(image - .windows(b"answer.dll".len()) - .any(|bytes| bytes == b"answer.dll")); + assert!(image.windows(b"answer.dll".len()).any(|bytes| bytes == b"answer.dll")); let original_dll = fs::read(&dll).unwrap(); let original_pdb = fs::read(&custom_pdb).unwrap(); command.arg("-Clink-arg=/EXPORT:missing_symbol"); @@ -6914,21 +6293,9 @@ fn explicit_entry_uses_the_selected_linker_dialect_once() { ("x86_64-pc-windows-msvc", None, "\"/ENTRY:wave_start\""), ("aarch64-pc-windows-msvc", None, "\"/ENTRY:wave_start\""), ("x86_64-unknown-linux-gnu", None, "\"-e\",\"wave_start\""), - ( - "x86_64-unknown-linux-gnu", - Some("ld.lld"), - "\"-e\",\"wave_start\"", - ), - ( - "x86_64-unknown-linux-gnu", - Some("clang"), - "\"-Wl,-e,wave_start\"", - ), - ( - "x86_64-pc-windows-msvc", - Some("link.exe"), - "\"/ENTRY:wave_start\"", - ), + ("x86_64-unknown-linux-gnu", Some("ld.lld"), "\"-e\",\"wave_start\""), + ("x86_64-unknown-linux-gnu", Some("clang"), "\"-Wl,-e,wave_start\""), + ("x86_64-pc-windows-msvc", Some("link.exe"), "\"/ENTRY:wave_start\""), ("aarch64-apple-darwin", None, "\"-e\",\"wave_start\""), ("wasm32-unknown-unknown", None, "\"--entry=wave_start\""), ]; @@ -6951,19 +6318,10 @@ fn explicit_entry_uses_the_selected_linker_dialect_once() { command.arg(format!("-Clinker={linker}")); } let output = command.output().unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); let plan = String::from_utf8(output.stdout).unwrap(); - let link_args = plan - .split("\"args\":[") - .nth(1) - .unwrap() - .split("],\"command\":") - .next() - .unwrap(); + let link_args = + plan.split("\"args\":[").nth(1).unwrap().split("],\"command\":").next().unwrap(); assert!(link_args.contains(expected), "{target}/{linker:?}: {plan}"); assert_eq!(link_args.matches("wave_start").count(), 1, "{plan}"); } @@ -7028,11 +6386,7 @@ fn msvc_native_fixtures_compile_and_arm64_hfas_match_clang() { .arg(output.join("stack.o")) .output() .expect("llvm-readobj is required for stack contracts"); - assert!( - unwind.status.success(), - "{}", - String::from_utf8_lossy(&unwind.stderr) - ); + assert!(unwind.status.success(), "{}", String::from_utf8_lossy(&unwind.stderr)); let unwind = String::from_utf8_lossy(&unwind.stdout); assert!( unwind.contains("RuntimeFunction") && unwind.contains("__chkstk"), @@ -7047,13 +6401,8 @@ fn msvc_native_fixtures_compile_and_arm64_hfas_match_clang() { line.starts_with("define ") && line.contains(&format!("@{name}(")) }) .unwrap_or_else(|| panic!("missing {name}: {ir}")); - let group = definition - .split('#') - .nth(1) - .unwrap() - .split_whitespace() - .next() - .unwrap(); + let group = + definition.split('#').nth(1).unwrap().split_whitespace().next().unwrap(); let attributes = ir .lines() .find(|line| line.starts_with(&format!("attributes #{group} ="))) @@ -7076,11 +6425,7 @@ fn msvc_native_fixtures_compile_and_arm64_hfas_match_clang() { .arg(&c_ir) .output() .unwrap(); - assert!( - result.status.success(), - "{}", - String::from_utf8_lossy(&result.stderr) - ); + assert!(result.status.success(), "{}", String::from_utf8_lossy(&result.stderr)); if target.starts_with("aarch64") { let c = fs::read_to_string(c_ir).unwrap(); let wave = fs::read_to_string(output.join("abi.ll")).unwrap(); @@ -7091,10 +6436,7 @@ fn msvc_native_fixtures_compile_and_arm64_hfas_match_clang() { "{wave}" ); assert!(wave.contains("declare %Hda @c_hda([4 x double])"), "{wave}"); - assert!( - wave.contains("define %Hda @wave_hda([4 x double]"), - "{wave}" - ); + assert!(wave.contains("define %Hda @wave_hda([4 x double]"), "{wave}"); // HFA exhaustion must move the aggregate together onto the stack. assert!(wave.contains("float, [4 x float], float)"), "{wave}"); } @@ -7116,10 +6458,9 @@ extern(system) fun native_api(value: i32) -> i32; fun main() -> i32 { return 0; } "#, ); - for (target, machine) in [ - ("x86_64-pc-windows-msvc", 0x8664u16), - ("aarch64-pc-windows-msvc", 0xaa64u16), - ] { + for (target, machine) in + [("x86_64-pc-windows-msvc", 0x8664u16), ("aarch64-pc-windows-msvc", 0xaa64u16)] + { if llvm::codegen::target::target_spec_for_triple(target).is_none() { continue; } @@ -7177,10 +6518,7 @@ fun main() -> i32 { return 0; } assert_eq!(&image[..2], b"MZ"); let pe = u32::from_le_bytes(image[0x3c..0x40].try_into().unwrap()) as usize; assert_eq!(&image[pe..pe + 4], b"PE\0\0"); - assert_eq!( - u16::from_le_bytes(image[pe + 4..pe + 6].try_into().unwrap()), - machine - ); + assert_eq!(u16::from_le_bytes(image[pe + 4..pe + 6].try_into().unwrap()), machine); } } } @@ -7199,12 +6537,7 @@ fn async_tasks_suspend_resume_cancel_and_exchange_tcp_data() { &PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("std"), &home.join(".wave/lib/wave/std"), ); - for name in [ - "lazy_nested", - "control_flow", - "timer_fairness", - "tcp_roundtrip", - ] { + for name in ["lazy_nested", "control_flow", "timer_fairness", "tcp_roundtrip"] { let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join(format!("tests/fixtures/async/{name}.wave")); let output = dir.join(name); @@ -7216,11 +6549,7 @@ fn async_tasks_suspend_resume_cancel_and_exchange_tcp_data() { .arg(&output) .output() .unwrap(); - assert!( - compiled.status.success(), - "{name}: {}", - String::from_utf8_lossy(&compiled.stderr) - ); + assert!(compiled.status.success(), "{name}: {}", String::from_utf8_lossy(&compiled.stderr)); let mut child = Command::new(&output).spawn().unwrap(); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(15); loop { @@ -7373,11 +6702,7 @@ fn executor_restart_fixture_emits_for_windows_msvc() { String::from_utf8_lossy(&compiled.stderr) ); let object = fs::read(output).unwrap(); - let expected = if target.starts_with("aarch64") { - 0xaa64 - } else { - 0x8664 - }; + let expected = if target.starts_with("aarch64") { 0xaa64 } else { 0x8664 }; assert_eq!(u16::from_le_bytes([object[0], object[1]]), expected); } fs::remove_dir_all(dir).unwrap(); @@ -7405,10 +6730,7 @@ fn msvc_rejects_foreign_link_input_before_launch_and_preserves_output() { OsStr::new("-o"), output.as_os_str(), ]); - assert!( - error.contains("foreign.obj") && error.contains("machine"), - "{error}" - ); + assert!(error.contains("foreign.obj") && error.contains("machine"), "{error}"); assert_eq!(fs::read(output).unwrap(), b"retained executable"); } @@ -7453,10 +6775,9 @@ fn darwin_process_and_timed_socket_fixtures_preserve_raw_syscall_contracts() { let dir = temp_case_dir("darwin-process-socket"); let home = dir.join("home"); copy_tree(&root.join("std"), &home.join(".wave/lib/wave/std")); - for (target, result_registers) in [ - ("x86_64-apple-darwin", "={rax},={rdx}"), - ("aarch64-apple-darwin", "={x0},={x1}"), - ] { + for (target, result_registers) in + [("x86_64-apple-darwin", "={rax},={rdx}"), ("aarch64-apple-darwin", "={x0},={x1}")] + { if llvm::codegen::target::target_spec_for_triple(target).is_none() { continue; } @@ -7481,11 +6802,7 @@ fn darwin_process_and_timed_socket_fixtures_preserve_raw_syscall_contracts() { assert!(out.join(format!("{source}.o")).is_file()); if opt == "-O0" { let ir = fs::read_to_string(out.join(format!("{source}.ll"))).unwrap(); - let suffix = if source == "fork_status" { - "_fork(" - } else { - "_sendto(" - }; + let suffix = if source == "fork_status" { "_fork(" } else { "_sendto(" }; let function = ir .split("\ndefine ") .find(|body| body.lines().next().unwrap_or("").contains(suffix)) @@ -7534,10 +6851,7 @@ fn retired_windows_gnu_targets_report_the_msvc_replacement() { OsStr::new(target), OsStr::new("--emit=obj"), ]); - assert!( - error.contains("has been retired") && error.contains(replacement), - "{error}" - ); + assert!(error.contains("has been retired") && error.contains(replacement), "{error}"); } } @@ -7608,16 +6922,9 @@ fn phase1_literal_printing_preserves_non_utf8_bytes() { OsStr::new("--out-dir"), dir.join(opt).as_os_str(), ]); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - let expected: &[u8] = if cfg!(windows) { - b"\xff\xc3\xa9\x80 42\r\n" - } else { - b"\xff\xc3\xa9\x80 42\n" - }; + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); + let expected: &[u8] = + if cfg!(windows) { b"\xff\xc3\xa9\x80 42\r\n" } else { b"\xff\xc3\xa9\x80 42\n" }; assert_eq!(output.stdout, expected); } } @@ -7626,26 +6933,15 @@ fn phase1_literal_printing_preserves_non_utf8_bytes() { fn main_parameter_contract_is_shared_by_hosted_freestanding_and_wasi_targets() { let dir = temp_case_dir("main-parameter-contract"); let mut targets = vec!["x86_64-unknown-linux-gnu", "x86_64-unknown-none-elf"]; - if cfg!(any( - feature = "llvm-target-wasm", - feature = "llvm-target-all" - )) { + if cfg!(any(feature = "llvm-target-wasm", feature = "llvm-target-all")) { targets.push("wasm32-wasip1"); } for target in targets { for (name, body, valid) in [ ("void_entry", "fun main() {}", true), ("int_entry", "fun main() -> i32 { return 0; }", true), - ( - "parameter_entry", - "fun main(x: i32) -> i32 { return x; }", - false, - ), - ( - "default_entry", - "fun main(x: i32 = 42) -> i32 { return x; }", - false, - ), + ("parameter_entry", "fun main(x: i32) -> i32 { return x; }", false), + ("default_entry", "fun main(x: i32 = 42) -> i32 { return x; }", false), ] { let source = write_wave(&dir, &format!("{name}.wave"), body); let out = dir.join(target).join(name); @@ -7680,27 +6976,11 @@ fn phase1_diagnostics_are_located_in_human_and_json_modes() { for newline in ["\n", "\r\n", "\r"] { for (body, message, code) in [ ("fun f() { const x: i32 = 1; }", "top level", "E2001"), - ( - "fun f() { for (static x: i32 = 1; x < 2; x += 1) {} }", - "top level", - "E2001", - ), + ("fun f() { for (static x: i32 = 1; x < 2; x += 1) {} }", "top level", "E2001"), ("fun f() { match (1) { 1 {} } }", "match arm", "E2001"), - ( - "extern(c) fun f(x: ptr<,>);", - "extern parameter type", - "E2001", - ), - ( - "extern(c) fun f() -> ptr<,>;", - "extern return type", - "E2001", - ), - ( - "fun main(x: i32 = 42) -> i32 { return x; }", - "must have zero parameters", - "E3001", - ), + ("extern(c) fun f(x: ptr<,>);", "extern parameter type", "E2001"), + ("extern(c) fun f() -> ptr<,>;", "extern return type", "E2001"), + ("fun main(x: i32 = 42) -> i32 { return x; }", "must have zero parameters", "E3001"), (r#"fun f() { var s: str = "\x00"; }"#, "NUL", "E1004"), ] { let source = write_wave(&dir, "bad.wave", &format!("// 한글{newline}{body}")); @@ -7711,11 +6991,7 @@ fn phase1_diagnostics_are_located_in_human_and_json_modes() { OsStr::new(&format!("--error-format={format}")), ]); assert!(!output.status.success(), "{body}"); - assert!( - output.stdout.is_empty(), - "unexpected parser output: {:?}", - output.stdout - ); + assert!(output.stdout.is_empty(), "unexpected parser output: {:?}", output.stdout); let stderr = String::from_utf8(output.stderr).unwrap(); assert!(stderr.contains(message), "{stderr}"); assert!(stderr.contains(code), "{stderr}"); @@ -7752,11 +7028,7 @@ fn addressed_array_literals_preserve_contextual_storage_at_o0_and_o2() { #[cfg(feature = "llvm-target-wasm")] fn wasi_access_checks_minimal_rights_and_preserves_host_errors() { for tool in ["node", "wasm-ld"] { - if !Command::new(tool) - .arg("--version") - .output() - .is_ok_and(|out| out.status.success()) - { + if !Command::new(tool).arg("--version").output().is_ok_and(|out| out.status.success()) { eprintln!("skipping WASI access execution: {tool} unavailable"); return; } @@ -7780,11 +7052,7 @@ fn wasi_access_checks_minimal_rights_and_preserves_host_errors() { .arg(&dir) .output() .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); let module = dir.join("access.wasm"); let link = Command::new("wasm-ld") .args(["--no-entry", "--export=check_access", "--export-memory"]) @@ -7793,11 +7061,7 @@ fn wasi_access_checks_minimal_rights_and_preserves_host_errors() { .arg(&module) .output() .unwrap(); - assert!( - link.status.success(), - "{}", - String::from_utf8_lossy(&link.stderr) - ); + assert!(link.status.success(), "{}", String::from_utf8_lossy(&link.stderr)); let result = Command::new("node") .arg(root.join("tests/fixtures/wasi_access/host.cjs")) .arg(&module) @@ -7818,34 +7082,19 @@ fn wasi_access_checks_minimal_rights_and_preserves_host_errors() { #[cfg(feature = "llvm-target-wasm")] fn wasm_missing_host_imports_have_actionable_human_and_json_errors() { for tool in ["node", "wasm-ld"] { - if !Command::new(tool) - .arg("--version") - .output() - .is_ok_and(|out| out.status.success()) - { + if !Command::new(tool).arg("--version").output().is_ok_and(|out| out.status.success()) { eprintln!("skipping WebAssembly missing-import execution: {tool} unavailable"); return; } } let dir = temp_case_dir("wasm-missing-imports"); let source = write_wave(&dir, "missing.wave", "extern(c) fun host_add(a: i32, b: i32) -> i32;\nfun main() -> i32 { return host_add(2, 3); }\n"); - for target in [ - "wasm32-unknown-unknown", - "wasm64-unknown-unknown", - "wasm32-wasip1", - ] { + for target in ["wasm32-unknown-unknown", "wasm64-unknown-unknown", "wasm32-wasip1"] { for format in ["human", "json"] { let output = wavec_command() .arg("build") .arg(&source) - .args([ - "--target", - target, - "--run", - "--error-format", - format, - "--out-dir", - ]) + .args(["--target", target, "--run", "--error-format", format, "--out-dir"]) .arg(dir.join(target)) .output() .unwrap(); @@ -7858,10 +7107,7 @@ fn wasm_missing_host_imports_have_actionable_human_and_json_errors() { }; assert!(stderr.contains(name), "{stderr}"); assert!(stderr.contains("explicit JavaScript host"), "{stderr}"); - assert!( - !stderr.contains("LinkError") && !stderr.contains("at async"), - "{stderr}" - ); + assert!(!stderr.contains("LinkError") && !stderr.contains("at async"), "{stderr}"); if format == "json" { let value = utils::wson::parse_json(stderr.trim()).unwrap(); let error = value.get("error").unwrap(); diff --git a/tests/frontend_regressions.rs b/tests/frontend_regressions.rs index c04b727d..1aa19ab4 100644 --- a/tests/frontend_regressions.rs +++ b/tests/frontend_regressions.rs @@ -14,11 +14,9 @@ fn directory() -> PathBuf { std::fs::create_dir_all(&path).unwrap(); path } + fn wave(args: &[&OsStr]) -> Output { - Command::new(env!("CARGO_BIN_EXE_wavec")) - .args(args) - .output() - .unwrap() + Command::new(env!("CARGO_BIN_EXE_wavec")).args(args).output().unwrap() } // Exercise the public host default, including MSVC on Windows. #[cfg(any(feature = "llvm-target-core64", feature = "llvm-target-all"))] @@ -36,14 +34,11 @@ fn frontend_target() -> String { .expect("at least one LLVM target must be enabled") .to_owned() } + fn check(path: &Path, target: &str) -> Output { - wave(&[ - OsStr::new("check"), - path.as_os_str(), - OsStr::new("--target"), - OsStr::new(target), - ]) + wave(&[OsStr::new("check"), path.as_os_str(), OsStr::new("--target"), OsStr::new(target)]) } + fn successful(output: &Output) { assert!( output.status.success(), @@ -59,11 +54,7 @@ fn imported_variants_respect_active_and_inactive_target_attributes() { let dir = directory(); let target = frontend_target(); let active_arch = target.split('-').next().unwrap(); - let inactive_arch = if active_arch == "wasm64" { - "arm64" - } else { - "wasm64" - }; + let inactive_arch = if active_arch == "wasm64" { "arm64" } else { "wasm64" }; let declarations = |value_arch: &str, missing_arch: &str| { format!( r#" @@ -120,14 +111,8 @@ fn json_diagnostics_preserve_imported_byte_ranges() { human.find('^').unwrap() < human.find("var after").unwrap(), "the marker must immediately follow the failing source line: {human}" ); - assert!( - stderr.contains(&format!("\"start\":{}", text.find("missing").unwrap())), - "{stderr}" - ); - assert!( - stderr.contains(&format!("\"end\":{}", text.find("missing").unwrap() + 7)), - "{stderr}" - ); + assert!(stderr.contains(&format!("\"start\":{}", text.find("missing").unwrap())), "{stderr}"); + assert!(stderr.contains(&format!("\"end\":{}", text.find("missing").unwrap() + 7)), "{stderr}"); } #[cfg(any(feature = "llvm-target-core64", feature = "llvm-target-all"))] @@ -173,19 +158,9 @@ fn pointer_sized_integer_ranges_follow_wasm_target_width() { let output = check(&source, "wasm32-unknown-unknown"); assert!(!output.status.success()); let stderr = String::from_utf8_lossy(&output.stderr); - assert!( - stderr.contains("u32") && !stderr.contains("panicked"), - "{stderr}" - ); - std::fs::write( - &source, - "fun word(x: isz) -> isz { return x; }\nfun main() {}\n", - ) - .unwrap(); - for (target, bits) in [ - ("wasm32-unknown-unknown", 32), - ("wasm64-unknown-unknown", 64), - ] { + assert!(stderr.contains("u32") && !stderr.contains("panicked"), "{stderr}"); + std::fs::write(&source, "fun word(x: isz) -> isz { return x; }\nfun main() {}\n").unwrap(); + for (target, bits) in [("wasm32-unknown-unknown", 32), ("wasm64-unknown-unknown", 64)] { let output_dir = dir.join(target); successful(&wave(&[ OsStr::new("build"), @@ -197,10 +172,7 @@ fn pointer_sized_integer_ranges_follow_wasm_target_width() { output_dir.as_os_str(), ])); let ir = std::fs::read_to_string(output_dir.join("word.ll")).unwrap(); - assert!( - ir.contains(&format!("define i{bits} @word(i{bits}")), - "{ir}" - ); + assert!(ir.contains(&format!("define i{bits} @word(i{bits}")), "{ir}"); } } @@ -224,17 +196,8 @@ fn never_returning_calls_lower_to_noreturn_and_unreachable() { let ir = std::fs::read_to_string(dir.join("never.ll")).unwrap(); assert!(ir.contains("define void @stop()"), "{ir}"); assert!(ir.contains("noreturn"), "{ir}"); - let value = ir - .split("define i32 @value()") - .nth(1) - .unwrap() - .split("\n}") - .next() - .unwrap(); - assert!( - value.contains("call void @stop()") && value.contains("unreachable"), - "{value}" - ); + let value = ir.split("define i32 @value()").nth(1).unwrap().split("\n}").next().unwrap(); + assert!(value.contains("call void @stop()") && value.contains("unreachable"), "{value}"); } #[test] @@ -274,11 +237,8 @@ fn windows_std_import_and_print_use_userprofile_without_home() { ), ) .unwrap(); - std::fs::write( - root.join("location_probe.wave"), - "pub fun located() -> i32 { return 17; }\n", - ) - .unwrap(); + std::fs::write(root.join("location_probe.wave"), "pub fun located() -> i32 { return 17; }\n") + .unwrap(); let source = profile.join("probe.wave"); std::fs::write( &source, @@ -292,16 +252,9 @@ fn windows_std_import_and_print_use_userprofile_without_home() { }; let printed = command().args(["print", "std-path"]).output().unwrap(); successful(&printed); - assert_eq!( - String::from_utf8(printed.stdout).unwrap().trim(), - root.to_string_lossy() - ); - let imported = command() - .arg("check") - .arg(source) - .args(["--target", &frontend_target()]) - .output() - .unwrap(); + assert_eq!(String::from_utf8(printed.stdout).unwrap().trim(), root.to_string_lossy()); + let imported = + command().arg("check").arg(source).args(["--target", &frontend_target()]).output().unwrap(); successful(&imported); std::fs::remove_dir_all(profile).unwrap(); } @@ -318,12 +271,7 @@ fn control_header_diagnostics_agree_in_human_and_json_output() { ("if (1) {} else if (0 {}", "else if header", "')'", "{"), ("if (1) {} else return;", "else header", "'{'", "return"), ("while (1 {}", "while header", "')'", "{"), - ( - "for (i = 0 i < 2; i += 1) {}", - "for initializer", - "';'", - "i <", - ), + ("for (i = 0 i < 2; i += 1) {}", "for initializer", "';'", "i <"), ] { let text = format!("fun broken() {{\n {body}\n}}\n"); std::fs::write(&library, &text).unwrap(); @@ -337,35 +285,19 @@ fn control_header_diagnostics_agree_in_human_and_json_output() { ]); for output in [&human, &json] { assert!(!output.status.success()); - assert!( - output.stdout.is_empty(), - "legacy parser output: {:?}", - output.stdout - ); + assert!(output.stdout.is_empty(), "legacy parser output: {:?}", output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("E2001"), "{stderr}"); assert!(stderr.contains("broken.wave"), "{stderr}"); - assert!( - stderr.contains(&format!("expected {expected} in {context}")), - "{stderr}" - ); - assert!( - !stderr.contains("failed to parse function declaration"), - "{stderr}" - ); + assert!(stderr.contains(&format!("expected {expected} in {context}")), "{stderr}"); + assert!(!stderr.contains("failed to parse function declaration"), "{stderr}"); } let json = String::from_utf8_lossy(&json.stderr); let start = text.find(body).unwrap() + body.rfind(unexpected).unwrap(); assert_eq!(json.lines().count(), 1, "{json}"); assert!(json.contains(&format!("\"start\":{start},")), "{json}"); - assert!( - json.contains(&format!("\"expected\":[\"{expected}\"]")), - "{json}" - ); - assert!( - json.contains(&format!("\"context\":\"{context}\"")), - "{json}" - ); + assert!(json.contains(&format!("\"expected\":[\"{expected}\"]")), "{json}"); + assert!(json.contains(&format!("\"context\":\"{context}\"")), "{json}"); } } @@ -409,10 +341,7 @@ fn lexer_escape_diagnostics_keep_labels_and_point_at_the_escape() { let target = frontend_target(); for (escape, label) in [ ("\\q", "unsupported escape sequence"), - ( - "\\xGG", - "hex escapes must be exactly two hexadecimal digits", - ), + ("\\xGG", "hex escapes must be exactly two hexadecimal digits"), ] { let text = format!("// 한글\r\nfun main() {{ \"앞{escape}\"; }}\r\n"); std::fs::write(&source, &text).unwrap(); @@ -433,10 +362,7 @@ fn lexer_escape_diagnostics_keep_labels_and_point_at_the_escape() { assert!(json.contains(&format!("\"label\":\"{label}\"")), "{json}"); let start = text.find(escape).unwrap(); assert!( - json.contains(&format!( - "\"start\":{start},\"end\":{}", - start + escape.len() - )), + json.contains(&format!("\"start\":{start},\"end\":{}", start + escape.len())), "{json}" ); assert!(json.contains("\"code\":\"E1004\""), "{json}"); @@ -497,19 +423,13 @@ fn imported_expression_errors_keep_their_location_in_both_output_formats() { assert!(!output.status.success()); assert!(output.stdout.is_empty()); let stderr = String::from_utf8_lossy(&output.stderr); - assert!( - stderr.contains(&format!("expected {expected} in {context}")), - "{stderr}" - ); + assert!(stderr.contains(&format!("expected {expected} in {context}")), "{stderr}"); assert!(stderr.contains("broken.wave"), "{stderr}"); assert!(stderr.contains("E2001"), "{stderr}"); } let stderr = String::from_utf8_lossy(&json.stderr); assert!(stderr.contains(&format!("\"start\":{start},")), "{stderr}"); - assert!( - stderr.contains(&format!("\"context\":\"{context}\"")), - "{stderr}" - ); + assert!(stderr.contains(&format!("\"context\":\"{context}\"")), "{stderr}"); } } @@ -563,11 +483,9 @@ fn eof_diagnostics_link_the_unmatched_opener_in_main_and_imported_sources() { let source = dir.join("main.wave"); let library = dir.join("unclosed.wave"); let target = frontend_target(); - for (marked, closer) in [ - ("fun f@(", ")"), - ("fun f() @{\r\n", "}"), - ("fun f() { call(@[1,", "]"), - ] { + for (marked, closer) in + [("fun f@(", ")"), ("fun f() @{\r\n", "}"), ("fun f() { call(@[1,", "]")] + { let marked = format!("// 한글\r\n{marked}"); let opener = marked.find('@').unwrap(); let text = marked.replace('@', ""); @@ -586,20 +504,14 @@ fn eof_diagnostics_link_the_unmatched_opener_in_main_and_imported_sources() { let json = utils::wson::parse_json(&String::from_utf8(output.stderr).unwrap()).unwrap(); let error = json.get("error").unwrap(); assert_eq!(error.get_str("code"), Some("E2001")); - assert_eq!( - error.get("span").unwrap().get_num("start"), - Some(text.len() as f64) - ); + assert_eq!(error.get("span").unwrap().get_num("start"), Some(text.len() as f64)); let related = error.get_arr("related").unwrap(); assert_eq!(related.len(), 1); assert!(related[0] .get_str("message") .unwrap() .contains(&format!("expected '{closer}'"))); - assert_eq!( - related[0].get("span").unwrap().get_num("start"), - Some(opener as f64) - ); + assert_eq!(related[0].get("span").unwrap().get_num("start"), Some(opener as f64)); assert!(related[0] .get("span") .unwrap() @@ -703,11 +615,7 @@ fn backend_errors_keep_imported_spans_without_panic_message_guessing() { let source = dir.join("main.wave"); let text = "pub fun helper() { asm { in(\"invalid_register\") 1 } }"; std::fs::write(&library, text).unwrap(); - std::fs::write( - &source, - "import(\"./assembly\")::{helper}; fun main() { helper(); }", - ) - .unwrap(); + std::fs::write(&source, "import(\"./assembly\")::{helper}; fun main() { helper(); }").unwrap(); for format in ["human", "json"] { let output = wave(&[ OsStr::new(&format!("--error-format={format}")), @@ -727,10 +635,7 @@ fn backend_errors_keep_imported_spans_without_panic_message_guessing() { && diagnostic.contains("lowering-validation"), "{diagnostic}" ); - assert!( - !diagnostic.contains("panic") && !diagnostic.contains("inferred"), - "{diagnostic}" - ); + assert!(!diagnostic.contains("panic") && !diagnostic.contains("inferred"), "{diagnostic}"); if format == "json" { assert_eq!(diagnostic.lines().count(), 1); } @@ -789,14 +694,8 @@ fn terminal_carets_use_display_cells_without_changing_json_locations() { .unwrap(); assert!(!human.status.success()); let text = String::from_utf8(human.stderr).unwrap(); - assert!( - text.contains(" 2 | println(\"한e\u{301}\"); @"), - "{text}" - ); - assert!( - text.contains(&format!(" | {}^", " ".repeat(20))), - "{text}" - ); + assert!(text.contains(" 2 | println(\"한e\u{301}\"); @"), "{text}"); + assert!(text.contains(&format!(" | {}^", " ".repeat(20))), "{text}"); let json = wave(&[ OsStr::new("check"), path.as_os_str(), @@ -809,14 +708,8 @@ fn terminal_carets_use_display_cells_without_changing_json_locations() { let error = parsed.get("error").unwrap(); assert_eq!(error.get_num("column"), Some(18.0)); let span = error.get("span").unwrap(); - assert_eq!( - span.get_num("start"), - Some(source.find('@').unwrap() as f64) - ); - assert_eq!( - span.get_num("end"), - Some((source.find('@').unwrap() + 1) as f64) - ); + assert_eq!(span.get_num("start"), Some(source.find('@').unwrap() as f64)); + assert_eq!(span.get_num("end"), Some((source.find('@').unwrap() + 1) as f64)); assert_eq!(span.get_num("column"), Some(18.0)); assert_eq!(span.get_num("end_column"), Some(19.0)); let _ = std::fs::remove_dir_all(dir); @@ -827,9 +720,8 @@ fn expression_depth_is_bounded_across_check_build_and_ast() { let dir = directory(); let target = frontend_target(); let source = dir.join("depth.wave"); - for kind in [ - "group", "unary", "bitwise", "binary", "cast", "call", "array", "field", "struct", - ] { + for kind in ["group", "unary", "bitwise", "binary", "cast", "call", "array", "field", "struct"] + { for depth in [128, 129, 5000] { let expr = match kind { "group" => format!("{}1{}", "(".repeat(depth), ")".repeat(depth)), @@ -862,10 +754,11 @@ fn expression_depth_is_bounded_across_check_build_and_ast() { }; #[cfg(not(target_os = "linux"))] let mut command = Command::new(env!("CARGO_BIN_EXE_wavec")); - command - .args([if entry == "ast" { "build" } else { entry }]) - .arg(&source) - .args(["--target", &target, "--error-format=json"]); + command.args([if entry == "ast" { "build" } else { entry }]).arg(&source).args([ + "--target", + &target, + "--error-format=json", + ]); if entry != "check" { command.arg("--out-dir").arg(&dir); } @@ -898,14 +791,8 @@ fn expression_depth_is_bounded_across_check_build_and_ast() { ); let json = utils::wson::parse_json(diagnostic.trim()).unwrap(); assert!(json.get("error").is_some(), "{diagnostic}"); - assert!( - diagnostic.contains("depth.wave"), - "missing source: {diagnostic}" - ); - assert!( - diagnostic.contains("\"line\""), - "missing location: {diagnostic}" - ); + assert!(diagnostic.contains("depth.wave"), "missing source: {diagnostic}"); + assert!(diagnostic.contains("\"line\""), "missing location: {diagnostic}"); } } } diff --git a/tests/io_constant_regressions.rs b/tests/io_constant_regressions.rs index 3ffdce4d..6837d3ad 100644 --- a/tests/io_constant_regressions.rs +++ b/tests/io_constant_regressions.rs @@ -24,11 +24,7 @@ fn compile(source: &str, optimization: &str) -> (PathBuf, String) { .arg(&dir) .output() .unwrap(); - assert!( - output.status.success(), - "{}\n{source}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}\n{source}", String::from_utf8_lossy(&output.stderr)); let ir = fs::read_to_string(dir.join("case.ll")).unwrap(); (dir, ir) } @@ -40,12 +36,7 @@ fn execute(dir: &std::path::Path, input: &str) -> Output { .stderr(Stdio::piped()) .spawn() .unwrap(); - child - .stdin - .take() - .unwrap() - .write_all(input.as_bytes()) - .unwrap(); + child.stdin.take().unwrap().write_all(input.as_bytes()).unwrap(); child.wait_with_output().unwrap() } @@ -55,9 +46,7 @@ fn run_both(source: &str, input: &str, expected: &str) { let output = execute(&dir, input); assert!(output.status.success(), "{opt}: {output:?}"); assert_eq!( - String::from_utf8(output.stdout) - .unwrap() - .replace("\r\n", "\n"), + String::from_utf8(output.stdout).unwrap().replace("\r\n", "\n"), expected, "{opt}" ); @@ -100,6 +89,7 @@ fn power_of_two(bits: usize) -> String { } digits.iter().rev().map(|n| char::from(b'0' + n)).collect() } + fn minus_one(value: &str) -> String { let mut bytes = value.as_bytes().to_vec(); for digit in bytes.iter_mut().rev() { @@ -109,10 +99,7 @@ fn minus_one(value: &str) -> String { } *digit = b'9'; } - String::from_utf8(bytes) - .unwrap() - .trim_start_matches('0') - .to_string() + String::from_utf8(bytes).unwrap().trim_start_matches('0').to_string() } #[test] @@ -137,10 +124,8 @@ println("{{}} {{}}", s{bits}, unsigned{bits}); "# )); input.push_str(&format!("+{max} {}1\n{min} {umax}\n", "0".repeat(330))); - expected.push_str(&format!( - "{min} {umax} {}\n{max} 1\n{min} {umax}\n", - "f".repeat(bits / 4) - )); + expected + .push_str(&format!("{min} {umax} {}\n{max} 1\n{min} {umax}\n", "f".repeat(bits / 4))); } source.push_str("return 0; }"); run_both(&source, &input, &expected); diff --git a/tests/native_boundaries.rs b/tests/native_boundaries.rs index 47594c51..1e32a0b5 100644 --- a/tests/native_boundaries.rs +++ b/tests/native_boundaries.rs @@ -17,6 +17,7 @@ impl Case { fs::create_dir_all(&path).unwrap(); Self(path) } + fn command(&self) -> Command { let mut c = Command::new(env!("CARGO_BIN_EXE_wavec")); c.current_dir(&self.0).arg("--std-root").arg(repo("std")); @@ -31,6 +32,7 @@ impl Drop for Case { fn repo(path: &str) -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join(path) } + fn success(out: Output) { assert!( out.status.success(), @@ -69,21 +71,15 @@ fn windows_file_and_memory_boundaries() { .output() .unwrap(), ); - success( - Command::new(case.0.join("case.exe")) - .current_dir(&case.0) - .output() - .unwrap(), - ); + success(Command::new(case.0.join("case.exe")).current_dir(&case.0).output().unwrap()); } } #[cfg(target_os = "linux")] #[test] fn windows_boundary_native_api_mocks() { let case = Case::new(); - let mut provider = fs::read_to_string(repo("std/sys/windows/fs.wave")) - .unwrap() - .replace("\r\n", "\n"); + let mut provider = + fs::read_to_string(repo("std/sys/windows/fs.wave")).unwrap().replace("\r\n", "\n"); let path_import = "import(\"std::sys::windows::path_encoding\")::{WidePath, wide_path, release_path, wide_path_to_utf8};"; assert!(provider.contains(path_import)); provider = provider.replace(path_import, r#" @@ -100,11 +96,8 @@ fun wide_path_to_utf8(path: ptr, n: i32, dst: ptr, cap: i64) -> i64 { r .replace("extern(system,", "extern(c,"), ) .unwrap(); - fs::copy( - repo("tests/fixtures/native_boundaries/windows_mock.wave"), - case.0.join("case.wave"), - ) - .unwrap(); + fs::copy(repo("tests/fixtures/native_boundaries/windows_mock.wave"), case.0.join("case.wave")) + .unwrap(); success( Command::new("cc") .args(["-c", "-O2"]) @@ -132,11 +125,7 @@ fn non_utf8_cli_paths_report_errors_without_colliding_outputs() { // Validate argv before filesystem access: macOS CI rejects file creation // with these byte sequences. A valid file at their shared lossy rendering // also catches accidental compilation of a different source via decoding. - fs::write( - case.0.join("x\u{fffd}.wave"), - "fun main() -> i32 { return 0; }", - ) - .unwrap(); + fs::write(case.0.join("x\u{fffd}.wave"), "fun main() -> i32 { return 0; }").unwrap(); for byte in [0xfe, 0xff] { let name = std::ffi::OsString::from_vec(vec![b'x', byte, b'.', b'w', b'a', b'v', b'e']); for args in [ @@ -144,13 +133,8 @@ fn non_utf8_cli_paths_report_errors_without_colliding_outputs() { vec!["build", "--emit=obj", "-o", "output.o"], vec!["build", "--out-dir", "out"], ] { - let out = case - .command() - .args(args) - .arg(&name) - .arg("--error-format=json") - .output() - .unwrap(); + let out = + case.command().args(args).arg(&name).arg("--error-format=json").output().unwrap(); assert_eq!(out.status.code(), Some(2), "{out:?}"); let error = utils::wson::parse_json(String::from_utf8_lossy(&out.stderr).trim()).unwrap(); @@ -164,11 +148,7 @@ fn non_utf8_cli_paths_report_errors_without_colliding_outputs() { assert!(!case.0.join("out").exists()); } } - fs::write( - case.0.join("normal.wave"), - "fun main() -> i32 { return 0; }", - ) - .unwrap(); + fs::write(case.0.join("normal.wave"), "fun main() -> i32 { return 0; }").unwrap(); success( case.command() .args(["build", "normal.wave", "--emit=obj", "-o", "explicit.o"]) @@ -228,9 +208,7 @@ fn environment_short_reads_interruptions_and_failure_cleanup() { .output() .unwrap(), ); - let high = fs::read_to_string(repo("std/env/environ.wave")) - .unwrap() - .replace("\r\n", "\n"); + let high = fs::read_to_string(repo("std/env/environ.wave")).unwrap().replace("\r\n", "\n"); let high = high.replace("import(\"std::sys::env\")::{\n env_read,\n};", "extern(c, \"mock_env_read\") fun env_read(buf: ptr, cap: i64) -> i64;") .replace("import(\"std::sys::memory\")::{sys_alloc, sys_free};", "extern(c, \"mock_alloc\") fun sys_alloc(size: i64) -> ptr; extern(c, \"mock_free\") fun sys_free(p: ptr, size: i64) -> i64;"); assert!(!high.contains("std::sys::env") && !high.contains("std::sys::memory")); @@ -345,10 +323,8 @@ fn windows_environment_conversion_capacity_and_cleanup() { let provider = fs::read_to_string(repo("std/sys/windows/env.wave")) .unwrap() .replace("extern(system,", "extern(c,"); - let fixture = fs::read_to_string(repo( - "tests/fixtures/release_constants/windows_env_mock.wave", - )) - .unwrap(); + let fixture = + fs::read_to_string(repo("tests/fixtures/release_constants/windows_env_mock.wave")).unwrap(); fs::write(case.0.join("case.wave"), format!("{provider}\n{fixture}")).unwrap(); success( Command::new("cc") diff --git a/tests/native_providers.rs b/tests/native_providers.rs index 57d675ee..9aa7aab4 100644 --- a/tests/native_providers.rs +++ b/tests/native_providers.rs @@ -17,11 +17,10 @@ impl Case { fs::create_dir_all(&path).unwrap(); Self(path) } + fn compiler(&self) -> Command { let mut cmd = Command::new(env!("CARGO_BIN_EXE_wavec")); - cmd.current_dir(&self.0) - .arg("--std-root") - .arg(root().join("std")); + cmd.current_dir(&self.0).arg("--std-root").arg(root().join("std")); cmd } } @@ -33,12 +32,15 @@ impl Drop for Case { fn root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) } + fn fixture(name: &str) -> PathBuf { root().join("tests/fixtures/native_providers").join(name) } + fn supported(target: &str) -> bool { llvm::codegen::target::target_spec_for_triple(target).is_some() } + fn checked(cmd: &mut Command, context: &str, dir: &Path) { let stdout = dir.join("stdout.log"); let stderr = dir.join("stderr.log"); @@ -70,24 +72,19 @@ fn checked(cmd: &mut Command, context: &str, dir: &Path) { fs::read_to_string(stderr).unwrap() ); } + fn build(case: &Case, source: &Path, target: &str, opt: &str, output: &Path, object: bool) { checked( case.compiler() .arg("build") .arg(source) - .args([ - "--target", - target, - opt, - "--emit", - if object { "obj" } else { "bin" }, - "-o", - ]) + .args(["--target", target, opt, "--emit", if object { "obj" } else { "bin" }, "-o"]) .arg(output), &format!("{target} {opt} {} build", source.display()), &case.0, ); } + fn host_target() -> String { let os = match std::env::consts::OS { "macos" => "apple-darwin", @@ -164,10 +161,7 @@ fn loongarch_attribute_aliases_select_identical_declarations() { let source = case.0.join("alias.wave"); fs::write(&source, "#[target(arch=\" LoOnG64 \")]\nfun alias() -> i32 { return 7; }\n#[target(arch=\"loongarch64\")]\nfun canonical() -> i32 { return alias(); }\nfun main() -> i32 { return canonical(); }\n").unwrap(); checked( - case.compiler() - .arg("build") - .arg(source) - .args(["--target", target, "--emit=check"]), + case.compiler().arg("build").arg(source).args(["--target", target, "--emit=check"]), target, &case.0, ); @@ -212,21 +206,11 @@ fn native_provider_fixtures_compile_and_run_on_their_host() { "loongarch64-unknown-linux-gnu", ], ), - ( - "macos_memory.wave", - vec!["x86_64-apple-darwin", "aarch64-apple-darwin"], - ), - ( - "macos_event.wave", - vec!["x86_64-apple-darwin", "aarch64-apple-darwin"], - ), + ("macos_memory.wave", vec!["x86_64-apple-darwin", "aarch64-apple-darwin"]), + ("macos_event.wave", vec!["x86_64-apple-darwin", "aarch64-apple-darwin"]), ( "random.wave", - vec![ - "x86_64-pc-windows-msvc", - "aarch64-pc-windows-msvc", - "x86_64-unknown-freebsd", - ], + vec!["x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc", "x86_64-unknown-freebsd"], ), ] { for target in targets { @@ -252,11 +236,7 @@ fn native_provider_fixtures_compile_and_run_on_their_host() { // Exact OS failures and historical clock values are injected at the OS ABI // boundary. The production Wave bodies are copied unchanged except imports // and the system calling-convention spelling on the Linux test host. -#[cfg(all( - target_os = "linux", - target_arch = "x86_64", - feature = "llvm-target-x86" -))] +#[cfg(all(target_os = "linux", target_arch = "x86_64", feature = "llvm-target-x86"))] #[test] fn native_provider_os_boundary_failures() { let case = Case::new(); @@ -278,9 +258,7 @@ fn native_provider_os_boundary_failures() { let types = unix.find("pub struct UnixListener").unwrap(); let end_types = unix[types..].find("#[target").unwrap() + types; text += &unix[types..end_types]; - let windows = unix - .find("#[target(os=\"windows\")]\nfun _unix_socket") - .unwrap(); + let windows = unix.find("#[target(os=\"windows\")]\nfun _unix_socket").unwrap(); let start = windows + "#[target(os=\"windows\")]\n".len(); let end = unix[start..].find('\n').unwrap() + start; text += &unix[start..end]; @@ -312,12 +290,10 @@ extern(c) fun net_close(fd: i64) -> i64; ); } if name.ends_with("random") { - text += &fs::read_to_string(root().join("std/random/fill.wave")) - .unwrap() - .replace( - "import(\"std::sys::random\")::{sys_random_available, sys_random_read};", - "", - ); + text += &fs::read_to_string(root().join("std/random/fill.wave")).unwrap().replace( + "import(\"std::sys::random\")::{sys_random_available, sys_random_read};", + "", + ); } text += &fs::read_to_string(fixture(&format!("{name}_mock.wave"))).unwrap(); let source = case.0.join(format!("{name}.wave")); @@ -334,14 +310,7 @@ extern(c) fun net_close(fd: i64) -> i64; ); for opt in ["-O0", "-O2"] { let object = case.0.join("probe.o"); - build( - &case, - &source, - "x86_64-unknown-linux-gnu", - opt, - &object, - true, - ); + build(&case, &source, "x86_64-unknown-linux-gnu", opt, &object, true); let binary = case.0.join("probe"); checked( Command::new("clang") @@ -354,11 +323,7 @@ extern(c) fun net_close(fd: i64) -> i64; &format!("{name} {opt} link"), &case.0, ); - checked( - &mut Command::new(binary), - &format!("{name} {opt} run"), - &case.0, - ); + checked(&mut Command::new(binary), &format!("{name} {opt} run"), &case.0); } } } @@ -403,13 +368,7 @@ fn darwin_bidirectional_c_abi_fixtures() { checked( Command::new("clang") .arg(format!("--target={target}")) - .args([ - "-ffreestanding", - "-fno-builtin", - "-fno-stack-protector", - opt, - "-c", - ]) + .args(["-ffreestanding", "-fno-builtin", "-fno-stack-protector", opt, "-c"]) .arg(source.with_extension("c")) .arg("-o") .arg(&c_object), @@ -417,30 +376,15 @@ fn darwin_bidirectional_c_abi_fixtures() { &case.0, ); let object = case.0.join("wave.o"); - build( - &case, - &source.with_extension("wave"), - target, - opt, - &object, - true, - ); + build(&case, &source.with_extension("wave"), target, opt, &object, true); if required && target == host_target() { let binary = case.0.join("abi"); checked( - Command::new("clang") - .arg(&c_object) - .arg(&object) - .arg("-o") - .arg(&binary), + Command::new("clang").arg(&c_object).arg(&object).arg("-o").arg(&binary), &format!("{context} link"), &case.0, ); - checked( - &mut Command::new(binary), - &format!("{context} run"), - &case.0, - ); + checked(&mut Command::new(binary), &format!("{context} run"), &case.0); } } } @@ -473,24 +417,14 @@ fn full_range_trigonometry_matches_high_precision_references() { let rows: Vec> = include_str!("fixtures/native_providers/trig_reference.txt") .lines() .filter(|s| !s.starts_with('#')) - .map(|s| { - s.split_whitespace() - .map(|n| u64::from_str_radix(n, 16).unwrap()) - .collect() - }) + .map(|s| s.split_whitespace().map(|n| u64::from_str_radix(n, 16).unwrap()).collect()) .collect(); let mut source = String::from("import(\"std::math::trig\")::{SinCosF64, SinCosF32, sin_cos_f64, sin_cos_f32, sin_f64, cos_f64, tan_f64, sin_f32, cos_f32, tan_f32, wrap_angle_pi_f64, MATH_PI_F64};\nimport(\"std::math::float\")::{abs_f64, float_from_bits_f64, float_from_bits_f32, float_to_bits_f64, float_to_bits_f32, nan_f64, infinity_f64, is_nan_f64};\n"); - for (column, name) in ["inputs", "sines", "cosines", "tangents"] - .iter() - .enumerate() - { + for (column, name) in ["inputs", "sines", "cosines", "tangents"].iter().enumerate() { source += &format!( "static {name}: array = [{}];\n", rows.len(), - rows.iter() - .map(|r| r[column].to_string()) - .collect::>() - .join(",") + rows.iter().map(|r| r[column].to_string()).collect::>().join(",") ); } source += &format!("const CASES: i32 = {};\n", rows.len()); @@ -498,24 +432,13 @@ fn full_range_trigonometry_matches_high_precision_references() { include_str!("fixtures/native_providers/trig_reference_f32.txt") .lines() .filter(|s| !s.starts_with('#')) - .map(|s| { - s.split_whitespace() - .map(|n| u64::from_str_radix(n, 16).unwrap()) - .collect() - }) + .map(|s| s.split_whitespace().map(|n| u64::from_str_radix(n, 16).unwrap()).collect()) .collect(); - for (column, name) in ["inputs32", "sines32", "cosines32", "tangents32"] - .iter() - .enumerate() - { + for (column, name) in ["inputs32", "sines32", "cosines32", "tangents32"].iter().enumerate() { source += &format!( "static {name}: array = [{}];\n", single_rows.len(), - single_rows - .iter() - .map(|r| r[column].to_string()) - .collect::>() - .join(",") + single_rows.iter().map(|r| r[column].to_string()).collect::>().join(",") ); } source += &format!("const SINGLE_CASES: i32 = {};\n", single_rows.len()); @@ -538,14 +461,7 @@ fn native_wide_arithmetic_is_freestanding_and_matches_reference_values() { let case = Case::new(); for opt in ["-O0", "-O2"] { let output = case.0.join("wide.exe"); - build( - &case, - &fixture("wide_arithmetic.wave"), - &target, - opt, - &output, - false, - ); + build(&case, &fixture("wide_arithmetic.wave"), &target, opt, &output, false); checked(&mut Command::new(output), "native i128 runtime", &case.0); } } @@ -618,9 +534,7 @@ fn native_arithmetic_helpers_preserve_direct_lowering_and_private_symbol_ownersh "native direct arithmetic", &case.0, ); - assert!(!fs::read_to_string(case.0.join("direct.ll")) - .unwrap() - .contains("__wave.runtime.")); + assert!(!fs::read_to_string(case.0.join("direct.ll")).unwrap().contains("__wave.runtime.")); } fs::write(&source, "extern(c, \"__wave.runtime.udiv.i128.i128\") fun user_symbol() -> u64; export(c) fun first(a: u128, b: u128) -> u128 { return a / b; } export(c) fun second(a: u128, b: u128) -> u128 { return a / b; } export(c) fun user() -> u64 { return user_symbol(); }").unwrap(); checked( @@ -634,36 +548,21 @@ fn native_arithmetic_helpers_preserve_direct_lowering_and_private_symbol_ownersh ); let ir = fs::read_to_string(case.0.join("direct.ll")).unwrap(); assert_eq!( - ir.matches("define private i128 @__wave.runtime.udiv.i128.i128.") - .count(), + ir.matches("define private i128 @__wave.runtime.udiv.i128.i128.").count(), 1, "{ir}" ); - assert!( - ir.contains("declare i64 @__wave.runtime.udiv.i128.i128()"), - "{ir}" - ); + assert!(ir.contains("declare i64 @__wave.runtime.udiv.i128.i128()"), "{ir}"); assert!(!ir.contains("__wave.runtime.sdiv")); } -#[cfg(all( - target_os = "linux", - target_arch = "x86_64", - feature = "llvm-target-x86" -))] +#[cfg(all(target_os = "linux", target_arch = "x86_64", feature = "llvm-target-x86"))] #[test] fn native_i128_runtime_preserves_the_public_c_abi() { let case = Case::new(); for opt in ["-O0", "-O2"] { let object = case.0.join("wide.o"); - build( - &case, - &fixture("wide_abi.wave"), - &host_target(), - opt, - &object, - true, - ); + build(&case, &fixture("wide_abi.wave"), &host_target(), opt, &object, true); let binary = case.0.join("wide-c"); checked( Command::new("clang") @@ -695,10 +594,7 @@ fn network_error_values_remain_available_without_a_socket_provider() { continue; } checked( - case.compiler() - .arg("check") - .arg(&source) - .args(["--target", target]), + case.compiler().arg("check").arg(&source).args(["--target", target]), "portable network error values", &case.0, ); @@ -754,10 +650,6 @@ fun main() -> i32 { for opt in ["-O0", "-O2"] { let output = case.0.join("cwd-contract.exe"); build(&case, &source, &target, opt, &output, false); - checked( - &mut Command::new(output), - "getcwd provider convention", - &case.0, - ); + checked(&mut Command::new(output), "getcwd provider convention", &case.0); } } diff --git a/tests/release_constants.rs b/tests/release_constants.rs index 5b8da09b..fb03bd23 100644 --- a/tests/release_constants.rs +++ b/tests/release_constants.rs @@ -17,6 +17,7 @@ impl Case { fs::create_dir_all(&p).unwrap(); Self(p) } + fn command(&self) -> Command { let mut c = Command::new(env!("CARGO_BIN_EXE_wavec")); c.current_dir(&self.0) @@ -24,15 +25,18 @@ impl Case { .arg(Path::new(env!("CARGO_MANIFEST_DIR")).join("std")); c } + fn source(&self, source: &str) { fs::write(self.0.join("case.wave"), source).unwrap(); } + fn build(&self, opt: &str) -> Output { self.command() .args(["build", "case.wave", opt, "--emit=ir,bin", "-o", "case.exe"]) .output() .unwrap() } + fn run(&self, source: &str) { self.source(source); for opt in ["-O0", "-O2"] { @@ -55,6 +59,7 @@ fn ok(o: Output) { String::from_utf8_lossy(&o.stderr) ); } + fn native() -> bool { let o = Command::new(env!("CARGO_BIN_EXE_wavec")) .args(["print", "default-target"]) @@ -76,28 +81,13 @@ fn constant_errors_agree_between_check_and_build() { for (source, message) in [ ("const N: i32 = 1 / 0;", "division or remainder by zero"), ("const N: i32 = 1 % 0;", "division or remainder by zero"), - ( - "const N: i8 = (-128 as i8) / (-1 as i8);", - "signed division overflows", - ), + ("const N: i8 = (-128 as i8) / (-1 as i8);", "signed division overflows"), ("const N: i32 = 1 << 32;", "shift count"), ("const N: i32 = (0.0 / 0.0) as i32;", "NaN or infinity"), - ( - "fun f() -> i32 { return 3; } const N: i32 = f();", - "unsupported constant expression", - ), - ( - "static N: i32 = 1; const M: i32 = N;", - "unsupported constant expression", - ), - ( - "const A: array = [1]; const N: i32 = A[0];", - "unsupported constant expression", - ), - ( - "const P: ptr = null; const Q: ptr = P + 1;", - "unsupported constant expression", - ), + ("fun f() -> i32 { return 3; } const N: i32 = f();", "unsupported constant expression"), + ("static N: i32 = 1; const M: i32 = N;", "unsupported constant expression"), + ("const A: array = [1]; const N: i32 = A[0];", "unsupported constant expression"), + ("const P: ptr = null; const Q: ptr = P + 1;", "unsupported constant expression"), ( "fun f() -> bool { return true; } const N: bool = false && f();", "unsupported constant expression", @@ -112,10 +102,7 @@ fn constant_errors_agree_between_check_and_build() { .output() .unwrap(); let err = String::from_utf8_lossy(&out.stderr); - assert!( - !out.status.success() && err.contains(message), - "{source}: {out:?}" - ); + assert!(!out.status.success() && err.contains(message), "{source}: {out:?}"); if format == "json" { let value = utils::wson::parse_json(err.trim()).unwrap(); let error = value.get("error").unwrap(); @@ -137,10 +124,7 @@ fn cli_preserves_signal_and_regular_child_status() { case.source( "extern(c) fun raise(signal: i32) -> i32; fun main() -> i32 { raise(15); return 0; }", ); - for args in [ - vec!["run", "case.wave"], - vec!["build", "case.wave", "--run"], - ] { + for args in [vec!["run", "case.wave"], vec!["build", "case.wave", "--run"]] { let out = case.command().args(args).output().unwrap(); assert_eq!(out.status.code(), Some(143), "{out:?}"); } @@ -159,12 +143,7 @@ fn webassembly_mmap_rejects_without_allocator_effects() { .command() .arg("build") .arg(root.join("tests/fixtures/release_constants/mmap.wave")) - .args([ - "--target=wasm64-unknown-unknown", - "--emit=obj", - opt, - "--out-dir", - ]) + .args(["--target=wasm64-unknown-unknown", "--emit=obj", opt, "--out-dir"]) .arg(&case.0) .output() .unwrap()); diff --git a/tests/release_stabilization.rs b/tests/release_stabilization.rs index a0d5942e..8e9500ee 100644 --- a/tests/release_stabilization.rs +++ b/tests/release_stabilization.rs @@ -17,11 +17,13 @@ impl Case { fs::create_dir_all(&path).unwrap(); Self(path) } + fn command(&self) -> Command { let mut command = Command::new(env!("CARGO_BIN_EXE_wavec")); command.current_dir(&self.0).env("NO_COLOR", "1"); command } + fn write(&self, path: &str, text: &str) -> PathBuf { let path = self.0.join(path); fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -42,6 +44,7 @@ fn success(out: Output) { String::from_utf8_lossy(&out.stderr) ); } + fn rejected(out: Output, message: &str) { assert!(!out.status.success()); assert!( @@ -50,6 +53,7 @@ fn rejected(out: Output, message: &str) { String::from_utf8_lossy(&out.stderr) ); } + fn source() -> &'static str { "fun main() -> i32 { return 0; }\n" } @@ -88,10 +92,7 @@ fn output_aliases_preserve_all_input_bytes_before_any_emission() { .unwrap(), "aliases compiler input", ); - assert_eq!( - fs::read_to_string(ir).unwrap(), - "define i32 @other() { ret i32 0 }\n" - ); + assert_eq!(fs::read_to_string(ir).unwrap(), "define i32 @other() { ret i32 0 }\n"); assert!(!case.0.join("source_1.o").exists()); } @@ -138,10 +139,7 @@ fn symlink_and_imported_source_outputs_are_rejected() { ); let imported = "pub const VALUE: i32 = 0;\n"; case.write("dep.wave", imported); - case.write( - "main.wave", - "import(\"./dep.wave\")::{VALUE}; fun main() -> i32 { return VALUE; }", - ); + case.write("main.wave", "import(\"./dep.wave\")::{VALUE}; fun main() -> i32 { return VALUE; }"); rejected( case.command() .args(["build", "main.wave", "--emit=obj", "-o", "dep.wave"]) @@ -149,10 +147,7 @@ fn symlink_and_imported_source_outputs_are_rejected() { .unwrap(), "aliases compiler input", ); - assert_eq!( - fs::read_to_string(case.0.join("dep.wave")).unwrap(), - imported - ); + assert_eq!(fs::read_to_string(case.0.join("dep.wave")).unwrap(), imported); } fn stub_std(case: &Case, name: &str, value: i32) -> PathBuf { @@ -163,11 +158,7 @@ fn stub_std(case: &Case, name: &str, value: i32) -> PathBuf { root.join("manifest.json"), ) .unwrap(); - fs::write( - root.join("value.wave"), - format!("pub const VALUE: i32 = {value};"), - ) - .unwrap(); + fs::write(root.join("value.wave"), format!("pub const VALUE: i32 = {value};")).unwrap(); root } @@ -176,10 +167,8 @@ fn explicit_std_roots_are_isolated_and_never_fall_back() { let case = Case::new(); let first = stub_std(&case, "std 한글 one", 7); let second = stub_std(&case, "second", 9); - let input = case.write( - "main.wave", - "import(\"std::value\")::{VALUE}; fun main() -> i32 { return VALUE; }", - ); + let input = case + .write("main.wave", "import(\"std::value\")::{VALUE}; fun main() -> i32 { return VALUE; }"); for (root, value) in [(&first, "7"), (&second, "9")] { success( case.command() @@ -195,54 +184,29 @@ fn explicit_std_roots_are_isolated_and_never_fall_back() { assert!(ir.contains(&format!("ret i32 {value}")), "{ir}"); } rejected( - case.command() - .args(["--std-root", "missing", "check"]) - .arg(&input) - .output() - .unwrap(), + case.command().args(["--std-root", "missing", "check"]).arg(&input).output().unwrap(), "cannot resolve std root", ); - fs::write( - first.join("manifest.json"), - "{\"name\":\"std\",\"compatibility_revision\":999999}", - ) - .unwrap(); + fs::write(first.join("manifest.json"), "{\"name\":\"std\",\"compatibility_revision\":999999}") + .unwrap(); rejected( - case.command() - .arg("--std-root") - .arg(&first) - .args(["check", "main.wave"]) - .output() - .unwrap(), + case.command().arg("--std-root").arg(&first).args(["check", "main.wave"]).output().unwrap(), "compatibility revision", ); fs::remove_file(first.join("manifest.json")).unwrap(); rejected( - case.command() - .arg("--std-root") - .arg(&first) - .args(["check", "main.wave"]) - .output() - .unwrap(), + case.command().arg("--std-root").arg(&first).args(["check", "main.wave"]).output().unwrap(), "compatibility revision", ); fs::write(first.join("manifest.json"), "malformed").unwrap(); rejected( - case.command() - .arg("--std-root") - .arg(&first) - .args(["check", "main.wave"]) - .output() - .unwrap(), + case.command().arg("--std-root").arg(&first).args(["check", "main.wave"]).output().unwrap(), "invalid", ); // Explicit roots are checked even when the source has no std imports. case.write("plain.wave", source()); rejected( - case.command() - .args(["--std-root", "missing", "check", "plain.wave"]) - .output() - .unwrap(), + case.command().args(["--std-root", "missing", "check", "plain.wave"]).output().unwrap(), "cannot resolve std root", ); } @@ -255,12 +219,9 @@ fn constant_cycles_fail_in_the_frontend_but_forward_dags_remain_valid() { "const A: i32 = B; const B: i32 = C; const C: i32 = A; fun main() -> i32 { return A; }", ] { case.write("cycle.wave", source); - for (mode, format) in [ - ("check", "human"), - ("build", "human"), - ("check", "json"), - ("build", "json"), - ] { + for (mode, format) in + [("check", "human"), ("build", "human"), ("check", "json"), ("build", "json")] + { let mut command = case.command(); command.arg(format!("--error-format={format}")); command.args([mode, "cycle.wave"]); @@ -278,15 +239,8 @@ fn constant_cycles_fail_in_the_frontend_but_forward_dags_remain_valid() { } } case.write("dep.wave", "pub const A: i32 = B; const B: i32 = A;"); - case.write( - "imported.wave", - "import(\"./dep.wave\")::{A}; fun main() -> i32 { return A; }", - ); - let out = case - .command() - .args(["check", "imported.wave"]) - .output() - .unwrap(); + case.write("imported.wave", "import(\"./dep.wave\")::{A}; fun main() -> i32 { return A; }"); + let out = case.command().args(["check", "imported.wave"]).output().unwrap(); let error = String::from_utf8_lossy(&out.stderr); assert!(error.contains("dep.wave"), "{error}"); rejected(out, "constant dependency cycle"); @@ -296,15 +250,8 @@ fn constant_cycles_fail_in_the_frontend_but_forward_dags_remain_valid() { "dag.wave", "const B: i32 = D; const C: i32 = D; const D: i32 = 3; fun main() -> i32 { return B + C; }", ); - success( - case.command() - .args(["build", "dag.wave", "--emit=ir", "-O2"]) - .output() - .unwrap(), - ); - assert!(fs::read_to_string(case.0.join("dag.ll")) - .unwrap() - .contains("ret i32 6")); + success(case.command().args(["build", "dag.wave", "--emit=ir", "-O2"]).output().unwrap()); + assert!(fs::read_to_string(case.0.join("dag.ll")).unwrap().contains("ret i32 6")); } #[cfg(feature = "llvm-target-wasm")] @@ -316,10 +263,9 @@ fn webassembly_runtime_is_freestanding_and_wasi_retries_preserve_errors() { } let case = Case::new(); let root = Path::new(env!("CARGO_MANIFEST_DIR")); - for (fixture, stem, target) in [ - ("wasi_retry", "retry", "wasm32-wasip1"), - ("wasm_wide", "wide", "wasm64-unknown-unknown"), - ] { + for (fixture, stem, target) in + [("wasi_retry", "retry", "wasm32-wasip1"), ("wasm_wide", "wide", "wasm64-unknown-unknown")] + { for opt in ["-O0", "-O2"] { let output = case.0.join(format!("{fixture}{opt}")); success( @@ -354,12 +300,7 @@ fn webassembly_runtime_is_freestanding_and_wasi_retries_preserve_errors() { } } success( - linker - .arg(output.join(format!("{stem}.o"))) - .arg("-o") - .arg(&wasm) - .output() - .unwrap(), + linker.arg(output.join(format!("{stem}.o"))).arg("-o").arg(&wasm).output().unwrap(), ); let mut node = Command::new("node"); if target.starts_with("wasm64") { @@ -397,13 +338,7 @@ fn aggregate_interop(arch: &str, abi: Option<&str>) { success( Command::new("clang") .args(&clang_args) - .args([ - "-O0", - "-ffreestanding", - "-fno-builtin", - "-fno-stack-protector", - "-c", - ]) + .args(["-O0", "-ffreestanding", "-fno-builtin", "-fno-stack-protector", "-c"]) .arg(root.join("tests/fixtures/linux_case_runtime/start.c")) .arg("-o") .arg(&start) @@ -424,13 +359,7 @@ fn aggregate_interop(arch: &str, abi: Option<&str>) { success( Command::new("clang") .args(&clang_args) - .args([ - "-O0", - "-ffreestanding", - "-fno-builtin", - "-fno-stack-protector", - "-c", - ]) + .args(["-O0", "-ffreestanding", "-fno-builtin", "-fno-stack-protector", "-c"]) .arg(fixture.with_extension("c")) .arg("-o") .arg(&object) @@ -439,10 +368,12 @@ fn aggregate_interop(arch: &str, abi: Option<&str>) { ); for opt in ["-O0", "-O2"] { let mut compiler = case.command(); - compiler - .arg("build") - .arg(fixture.with_extension("wave")) - .args(["--target", &target, "--emit=obj", opt]); + compiler.arg("build").arg(fixture.with_extension("wave")).args([ + "--target", + &target, + "--emit=obj", + opt, + ]); if let Some(abi) = abi { compiler.arg(format!("--abi={abi}")); } @@ -463,12 +394,7 @@ fn aggregate_interop(arch: &str, abi: Option<&str>) { if std::env::consts::ARCH == arch && std::env::consts::OS == "linux" { success(Command::new(&binary).output().unwrap()); } else { - success( - Command::new(format!("qemu-{arch}")) - .arg(&binary) - .output() - .unwrap(), - ); + success(Command::new(format!("qemu-{arch}")).arg(&binary).output().unwrap()); } } } @@ -498,10 +424,7 @@ fn aarch64_aggregate_registers_match_clang() { #[test] fn loongarch_fp_features_match_emitted_instructions() { let case = Case::new(); - case.write( - "float.wave", - "export(c) fun add_double(a: f64, b: f64) -> f64 { return a + b; }", - ); + case.write("float.wave", "export(c) fun add_double(a: f64, b: f64) -> f64 { return a + b; }"); for abi in ["lp64s", "lp64f", "lp64d"] { success( case.command() @@ -548,49 +471,29 @@ fn loongarch_fp_features_match_emitted_instructions() { #[test] fn webassembly_runtime_helpers_are_private_unique_and_only_generated_when_used() { let case = Case::new(); - case.write( - "plain.wave", - "export(c) fun add(a: u64, b: u64) -> u64 { return a + b; }", - ); + case.write("plain.wave", "export(c) fun add(a: u64, b: u64) -> u64 { return a + b; }"); success( case.command() - .args([ - "build", - "plain.wave", - "--target=wasm64-unknown-unknown", - "--emit=ir", - ]) + .args(["build", "plain.wave", "--target=wasm64-unknown-unknown", "--emit=ir"]) .output() .unwrap(), ); - assert!(!fs::read_to_string(case.0.join("plain.ll")) - .unwrap() - .contains("__wave.runtime.")); + assert!(!fs::read_to_string(case.0.join("plain.ll")).unwrap().contains("__wave.runtime.")); // An application symbol with the same spelling remains an application import. case.write("wide.wave", "extern(c, \"__wave.runtime.udiv.i128.i128\") fun host_value() -> u64; export(c) fun divide(a: u128, b: u128) -> u128 { return a / b + a / b; } export(c) fun host() -> u64 { return host_value(); }"); success( case.command() - .args([ - "build", - "wide.wave", - "--target=wasm64-unknown-unknown", - "--emit=ir", - "-O0", - ]) + .args(["build", "wide.wave", "--target=wasm64-unknown-unknown", "--emit=ir", "-O0"]) .output() .unwrap(), ); let ir = fs::read_to_string(case.0.join("wide.ll")).unwrap(); assert_eq!( - ir.matches("define private i128 @__wave.runtime.udiv.i128.i128.") - .count(), + ir.matches("define private i128 @__wave.runtime.udiv.i128.i128.").count(), 1, "{ir}" ); - assert!( - ir.contains("declare i64 @__wave.runtime.udiv.i128.i128()"), - "{ir}" - ); + assert!(ir.contains("declare i64 @__wave.runtime.udiv.i128.i128()"), "{ir}"); assert!(ir.contains("\"wasm-import-module\"=\"env\"")); assert!(!ir.contains("__wave.runtime.sdiv")); } diff --git a/tests/runtime_regressions.rs b/tests/runtime_regressions.rs index c3abe5f3..fb5b1777 100644 --- a/tests/runtime_regressions.rs +++ b/tests/runtime_regressions.rs @@ -36,10 +36,7 @@ fn copy_tree(source: &Path, destination: &Path) { fn run_native_fixture(name: &str) { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let compiler = PathBuf::from(env!("CARGO_BIN_EXE_wavec")); - let target = Command::new(&compiler) - .args(["print", "default-target"]) - .output() - .unwrap(); + let target = Command::new(&compiler).args(["print", "default-target"]).output().unwrap(); assert!(target.status.success()); let host = String::from_utf8(target.stdout).unwrap().trim().to_owned(); if llvm::codegen::target::target_spec_for_triple(&host).is_none() { @@ -56,16 +53,8 @@ fn run_native_fixture(name: &str) { fs::create_dir_all(&directory.0).unwrap(); let home = directory.0.join("home"); copy_tree(&root.join("std"), &home.join(".wave/lib/wave/std")); - fs::copy( - root.join("tests/fixtures").join(name), - directory.0.join("source.wave"), - ) - .unwrap(); - let executable = directory.0.join(if cfg!(windows) { - "fixture.exe" - } else { - "fixture" - }); + fs::copy(root.join("tests/fixtures").join(name), directory.0.join("source.wave")).unwrap(); + let executable = directory.0.join(if cfg!(windows) { "fixture.exe" } else { "fixture" }); let mut command = Command::new(&compiler); command .env("HOME", &home) @@ -103,10 +92,7 @@ fn run_native_fixture(name: &str) { ); } -#[cfg(all( - target_os = "linux", - any(feature = "llvm-target-aarch64", feature = "llvm-target-all") -))] +#[cfg(all(target_os = "linux", any(feature = "llvm-target-aarch64", feature = "llvm-target-all")))] #[test] fn executor_imports_compile_with_a_one_mib_process_stack() { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); diff --git a/tests/stabilization_17.rs b/tests/stabilization_17.rs index cef22b33..24e642c8 100644 --- a/tests/stabilization_17.rs +++ b/tests/stabilization_17.rs @@ -17,6 +17,7 @@ impl Case { fs::create_dir_all(&p).unwrap(); Self(p) } + fn command(&self) -> Command { let mut c = Command::new(env!("CARGO_BIN_EXE_wavec")); c.current_dir(&self.0) @@ -24,15 +25,18 @@ impl Case { .arg(Path::new(env!("CARGO_MANIFEST_DIR")).join("std")); c } + fn source(&self, source: &str) { fs::write(self.0.join("case.wave"), source).unwrap(); } + fn build(&self, opt: &str) -> Output { self.command() .args(["build", "case.wave", opt, "--emit=ir,bin", "-o", "case.exe"]) .output() .unwrap() } + fn run(&self, source: &str) { self.source(source); for opt in ["-O0", "-O2"] { @@ -55,6 +59,7 @@ fn ok(o: Output) { String::from_utf8_lossy(&o.stderr) ); } + fn native() -> bool { let o = Command::new(env!("CARGO_BIN_EXE_wavec")) .args(["print", "default-target"]) @@ -92,10 +97,7 @@ fn declaration_diagnostics_preserve_the_offending_token() { .output() .unwrap(); let err = String::from_utf8_lossy(&o.stderr); - assert!( - !o.status.success() && o.stdout.is_empty(), - "{source}: {o:?}" - ); + assert!(!o.status.success() && o.stdout.is_empty(), "{source}: {o:?}"); assert!(err.contains(message), "{source}: {err}"); if format == "json" { let json = utils::wson::parse_json(err.trim()).unwrap(); @@ -124,12 +126,7 @@ fn cli_rejects_invalid_debug_modes_and_empty_dependency_roots() { vec!["--dep-root", ""], vec!["--dep-root", " "], ] { - let o = case - .command() - .args(args) - .args(["check", "case.wave"]) - .output() - .unwrap(); + let o = case.command().args(args).args(["check", "case.wave"]).output().unwrap(); assert_eq!(o.status.code(), Some(2), "{o:?}"); } for args in [ @@ -139,12 +136,7 @@ fn cli_rejects_invalid_debug_modes_and_empty_dependency_roots() { vec!["--dep-root", "relative"], vec!["--dep-root=/tmp"], ] { - ok(case - .command() - .args(args) - .args(["check", "case.wave"]) - .output() - .unwrap()); + ok(case.command().args(args).args(["check", "case.wave"]).output().unwrap()); } } #[test] @@ -175,16 +167,13 @@ fn numeric_constants_reject_invalid_shifts_and_casts() { utils::wson::parse_json(String::from_utf8_lossy(&o.stderr).trim()).unwrap(); } } + fn environment_fixture(provider: &str, fixture: &str) -> String { // Git's Windows checkout uses CRLF; never silently leave the real import // beside the mock declaration when replacing the provider boundary. let provider = provider.replace("\r\n", "\n").replace('\r', "\n"); let import = "import(\"std::sys::env\")::{\n env_read,\n};"; - assert_eq!( - provider.matches(import).count(), - 1, - "env_read provider import changed" - ); + assert_eq!(provider.matches(import).count(), 1, "env_read provider import changed"); format!("{}\n{fixture}", provider.replacen(import, "", 1)) } @@ -199,15 +188,8 @@ fn environment_fixture_replaces_the_provider_for_all_line_endings() { let fixture = fs::read_to_string(root.join("tests/fixtures/stabilization_17/environment.wave")).unwrap(); for newline in ["\n", "\r\n", "\r"] { - case.source(&environment_fixture( - &provider.replace('\n', newline), - &fixture, - )); - ok(case - .command() - .args(["check", "case.wave"]) - .output() - .unwrap()); + case.source(&environment_fixture(&provider.replace('\n', newline), &fixture)); + ok(case.command().args(["check", "case.wave"]).output().unwrap()); } } @@ -314,14 +296,7 @@ fn webassembly_checked_numeric_runtime() { link.arg("-mwasm64"); } link.arg("--no-entry"); - for name in [ - "shift", - "signed_shift", - "signed_cast", - "unsigned_cast", - "truth", - "wide", - ] { + for name in ["shift", "signed_shift", "signed_cast", "unsigned_cast", "truth", "wide"] { link.arg(format!("--export={name}")); } ok(link.arg(object).arg("-o").arg(&module).output().unwrap()); @@ -349,11 +324,7 @@ fn darwin_syscall_secondary_return_does_not_preserve_the_third_argument() { for count in 3..=6 { let mut args = vec![54, 1, 2, 7]; args.extend(4..=count); - let args = args - .iter() - .map(ToString::to_string) - .collect::>() - .join(", "); + let args = args.iter().map(ToString::to_string).collect::>().join(", "); body.push_str(&format!("if (syscall{count}({args}) != 7 || syscall{count}({args}) != 7) {{ return {count}; }}\n")); } body.push_str("return 0; }"); diff --git a/tests/std_io_regressions.rs b/tests/std_io_regressions.rs index 95634755..0a671a2a 100644 --- a/tests/std_io_regressions.rs +++ b/tests/std_io_regressions.rs @@ -6,10 +6,7 @@ use std::process::Command; #[test] fn native_standard_io_preserves_data_descriptors_and_deadlines() { let compiler = env!("CARGO_BIN_EXE_wavec"); - let target = Command::new(compiler) - .args(["print", "default-target"]) - .output() - .unwrap(); + let target = Command::new(compiler).args(["print", "default-target"]).output().unwrap(); assert!(target.status.success()); let host = String::from_utf8(target.stdout).unwrap().trim().to_owned(); if llvm::codegen::target::target_spec_for_triple(&host).is_none() { diff --git a/tests/whale_backend.rs b/tests/whale_backend.rs index c0dd81f1..2a48b700 100644 --- a/tests/whale_backend.rs +++ b/tests/whale_backend.rs @@ -16,6 +16,7 @@ impl Case { fs::write(root.join("main.wave"), source).unwrap(); Self { root } } + fn run(&self, args: &[&str]) -> Output { Command::new(env!("CARGO_BIN_EXE_wavec")) .current_dir(&self.root) @@ -23,13 +24,10 @@ impl Case { .output() .unwrap() } + fn ir(&self) -> String { let output = self.run(&["--whale", "build", "main.wave", "--emit=ir"]); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); fs::read_to_string(self.root.join("main.wir")).unwrap() } } @@ -63,17 +61,14 @@ fun main() -> i32 { "#, ); let ir = case.ir(); + assert!(ir.contains("format_version 3"), "{ir}"); assert!(ir.contains("x86_64-whale-linux"), "{ir}"); assert!(ir.contains("phi"), "{ir}"); assert!(ir.contains("call"), "{ir}"); assert!(ir.contains("while.test"), "{ir}"); assert!(!case.root.join("main.ll").exists()); let check = case.run(&["--whale", "check", "main.wave"]); - assert!( - check.status.success(), - "{}", - String::from_utf8_lossy(&check.stderr) - ); + assert!(check.status.success(), "{}", String::from_utf8_lossy(&check.stderr)); } #[test] @@ -94,10 +89,7 @@ fun main() -> i32 { return narrow(257); } assert!(trunc < extend, "{ir}"); assert!(ir.contains("bitcast"), "{ir}"); assert!(ir.contains("4294967296"), "{ir}"); - assert!( - ir.contains("340282366920938463463374607431768211455"), - "{ir}" - ); + assert!(ir.contains("340282366920938463463374607431768211455"), "{ir}"); assert!(ir.contains("ule"), "{ir}"); } @@ -124,37 +116,19 @@ fn unsupported_source_has_json_location_and_preserves_previous_output() { let case = Case::new("unsupported", "fun main() -> i32 {\n return 8 / 2;\n}\n"); let output = case.root.join("main.wir"); fs::write(&output, "previous verified IR").unwrap(); - let result = case.run(&[ - "--whale", - "build", - "main.wave", - "--emit=ir", - "--error-format=json", - ]); + let result = case.run(&["--whale", "build", "main.wave", "--emit=ir", "--error-format=json"]); assert!(!result.status.success()); let stderr = String::from_utf8_lossy(&result.stderr); let json = wson::parse_json(stderr.trim()).unwrap(); - assert!( - stderr.contains("Whale backend does not yet support operation Divide"), - "{json:?}" - ); + assert!(stderr.contains("Whale backend does not yet support operation Divide"), "{json:?}"); assert!(stderr.contains("main.wave"), "{json:?}"); assert!(stderr.contains("E4001"), "{json:?}"); assert_eq!(fs::read_to_string(output).unwrap(), "previous verified IR"); let targets = case.run(&["print", "target-list"]); assert!(targets.status.success()); let targets = String::from_utf8(targets.stdout).unwrap(); - let llvm = case.run(&[ - "check", - "main.wave", - "--target", - targets.lines().next().unwrap(), - ]); - assert!( - llvm.status.success(), - "{}", - String::from_utf8_lossy(&llvm.stderr) - ); + let llvm = case.run(&["check", "main.wave", "--target", targets.lines().next().unwrap()]); + assert!(llvm.status.success(), "{}", String::from_utf8_lossy(&llvm.stderr)); let whale = case.run(&["--whale", "check", "main.wave"]); assert!(!whale.status.success()); } @@ -165,12 +139,7 @@ fn unsupported_requests_fail_without_creating_outputs() { for args in [ vec!["--whale", "build", "main.wave"], vec!["--whale", "-O2", "build", "main.wave", "--emit=ir"], - vec![ - "--whale", - "--target=aarch64-unknown-linux-gnu", - "check", - "main.wave", - ], + vec!["--whale", "--target=aarch64-unknown-linux-gnu", "check", "main.wave"], vec!["--whale", "build", "main.wave", "--emit=ir", "--run"], vec!["--whale", "build", "main.wave", "--emit=obj"], vec!["--whale", "print", "supported-targets"], @@ -186,11 +155,7 @@ fn unsupported_requests_fail_without_creating_outputs() { assert!(!case.root.join("main.wir").exists()); assert!(!case.root.join("main.ll").exists()); let dry = case.run(&["--whale", "build", "main.wave", "--emit=ir", "--dry-run"]); - assert!( - dry.status.success(), - "{}", - String::from_utf8_lossy(&dry.stderr) - ); + assert!(dry.status.success(), "{}", String::from_utf8_lossy(&dry.stderr)); assert!(!case.root.join("main.wir").exists()); } @@ -198,68 +163,34 @@ fn unsupported_requests_fail_without_creating_outputs() { fn whale_output_cannot_overwrite_source_or_imported_input() { let source = "fun main() -> i32 { return 0; }"; let case = Case::new("output", source); - let result = case.run(&[ - "--whale", - "build", - "main.wave", - "--emit=ir", - "-o", - "main.wave", - ]); + let result = case.run(&["--whale", "build", "main.wave", "--emit=ir", "-o", "main.wave"]); assert!(!result.status.success()); - assert_eq!( - fs::read_to_string(case.root.join("main.wave")).unwrap(), - source - ); - fs::write( - case.root.join("helper.wave"), - "pub fun helper() -> i32 { return 1; }", - ) - .unwrap(); + assert_eq!(fs::read_to_string(case.root.join("main.wave")).unwrap(), source); + fs::write(case.root.join("helper.wave"), "pub fun helper() -> i32 { return 1; }").unwrap(); fs::write( case.root.join("main.wave"), "import(\"./helper.wave\")::{helper}; fun main() -> i32 { return helper(); }", ) .unwrap(); let original = fs::read_to_string(case.root.join("helper.wave")).unwrap(); - let result = case.run(&[ - "--whale", - "build", - "main.wave", - "--emit=ir", - "-o", - "helper.wave", - ]); + let result = case.run(&["--whale", "build", "main.wave", "--emit=ir", "-o", "helper.wave"]); assert!(!result.status.success()); assert!( String::from_utf8_lossy(&result.stderr).contains("aliases compiler input"), "{}", String::from_utf8_lossy(&result.stderr) ); - assert_eq!( - fs::read_to_string(case.root.join("helper.wave")).unwrap(), - original - ); + assert_eq!(fs::read_to_string(case.root.join("helper.wave")).unwrap(), original); } #[test] fn llvm_stays_default_and_whale_help_is_available() { let case = Case::new("default", "fun main() -> i32 { return 0; }"); if cfg!(feature = "llvm-target-x86") { - let output = case.run(&[ - "--target=x86_64-unknown-linux-gnu", - "build", - "main.wave", - "--emit=ir", - ]); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - assert!(fs::read_to_string(case.root.join("main.ll")) - .unwrap() - .contains("target triple")); + let output = + case.run(&["--target=x86_64-unknown-linux-gnu", "build", "main.wave", "--emit=ir"]); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); + assert!(fs::read_to_string(case.root.join("main.ll")).unwrap().contains("target triple")); assert!(!case.root.join("main.wir").exists()); } let help = case.run(&["--whale", "--help"]); @@ -315,3 +246,82 @@ fn whale_expression_depth_keeps_the_frontend_boundary() { } } } + +#[test] +fn checked_shifts_and_float_casts_emit_ir_for_literal_and_runtime_operands() { + let case = Case::new( + "checked-numeric", + r#" +fun left(x: u8, n: u128) -> u8 { return x << n; } +fun right(x: i64, n: i8) -> i64 { return x >> n; } +fun literal(x: u128) -> u128 { return x << (127); } +fun to_signed(f: f64) -> i8 { return f as i8; } +fun to_unsigned(f: f32) -> u128 { return f as u128; } +fun main() -> i32 { + var a: i8 = -128.9 as i8; + var b: u8 = -0.9 as u8; + if (a == -128 && b == 0) { return 0; } + return 1; +} +"#, + ); + let ir = case.ir(); + assert!(ir.contains("shift count out of range"), "{ir}"); + assert!(ir.contains("float-to-integer conversion out of range"), "{ir}"); + let check = case.run(&["--whale", "check", "main.wave"]); + assert!(check.status.success(), "{}", String::from_utf8_lossy(&check.stderr)); +} + +#[test] +fn invalid_numeric_constants_keep_frontend_diagnostics_and_previous_output() { + let case = Case::new("invalid-numeric", ""); + let output = case.root.join("main.wir"); + for source in [ + "fun main() -> i32 { return 1 << -1; }", + "fun main() -> i32 { return 1 << 32; }", + "fun main() -> i32 { return 1 << 4294967296; }", + "fun main() -> i32 { return 1 << 340282366920938463463374607431768211456; }", + "fun main() -> i32 { var x: u8 = 256.0 as u8; return 0; }", + "fun main() -> i32 { var x: i8 = -129.0 as i8; return 0; }", + "fun main() -> i32 { var x: u8 = -1.0 as u8; return 0; }", + "fun main() -> i32 { return 1 << true; }", + "fun main() -> i32 { return 1 << 1.0; }", + ] { + fs::write(case.root.join("main.wave"), source).unwrap(); + fs::write(&output, "previous verified IR").unwrap(); + for command in ["check", "build"] { + let mut args = vec!["--whale", command, "main.wave", "--error-format=json"]; + if command == "build" { + args.push("--emit=ir"); + } + let result = case.run(&args); + assert_eq!(result.status.code(), Some(1), "{source}"); + let stderr = String::from_utf8_lossy(&result.stderr); + wson::parse_json(stderr.trim()).unwrap(); + assert!(stderr.contains("main.wave"), "{source}: {stderr}"); + assert!(!stderr.contains("internal compiler error"), "{source}: {stderr}"); + assert!(!stderr.contains("does not yet support"), "{source}: {stderr}"); + assert_eq!(fs::read_to_string(&output).unwrap(), "previous verified IR"); + } + } +} + +#[test] +fn short_circuited_invalid_numeric_operations_stay_in_the_rhs_block() { + let case = Case::new( + "numeric-short-circuit", + r#" +fun main() -> i32 { + if (true || (1 << 340282366920938463463374607431768211456) == 0) { + if (false && (256.0 as u8) == 0) { return 1; } + if (true || (1 << -1) == 0) { return 0; } + } + return 2; +} +"#, + ); + let ir = case.ir(); + let branch = ir.find("cbr bool").expect(&ir); + let trap = ir.find("trap_if").expect(&ir); + assert!(branch < trap, "a skipped RHS must not trap in the entry block: {ir}"); +} diff --git a/tools/format-rust.py b/tools/format-rust.py new file mode 100755 index 00000000..336c4ad3 --- /dev/null +++ b/tools/format-rust.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 + +import argparse +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent + +SKIP_DIRS = { + ".git", + "target", + "vendor", +} + + +def rust_files(): + result = subprocess.run( + ["git", "ls-files", "*.rs"], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + + for name in result.stdout.splitlines(): + path = ROOT / name + + if any(part in SKIP_DIRS for part in path.parts): + continue + + if path.is_file(): + yield path + + +def strip_lexical_content(line, state): + result = [] + i = 0 + + while i < len(line): + if state["block_comment"] > 0: + if line.startswith("/*", i): + state["block_comment"] += 1 + result.extend(" ") + i += 2 + elif line.startswith("*/", i): + state["block_comment"] -= 1 + result.extend(" ") + i += 2 + else: + result.append(" ") + i += 1 + continue + + if state["string"]: + if state["escape"]: + state["escape"] = False + result.append(" ") + i += 1 + elif line[i] == "\\": + state["escape"] = True + result.append(" ") + i += 1 + elif line[i] == '"': + state["string"] = False + result.append(" ") + i += 1 + else: + result.append(" ") + i += 1 + continue + + if state["char"]: + if state["escape"]: + state["escape"] = False + result.append(" ") + i += 1 + elif line[i] == "\\": + state["escape"] = True + result.append(" ") + i += 1 + elif line[i] == "'": + state["char"] = False + result.append(" ") + i += 1 + else: + result.append(" ") + i += 1 + continue + + if line.startswith("//", i): + result.extend(" " * (len(line) - i)) + break + + if line.startswith("/*", i): + state["block_comment"] += 1 + result.extend(" ") + i += 2 + continue + + if line[i] == '"': + state["string"] = True + result.append(" ") + i += 1 + continue + + if line[i] == "'": + # Rust lifetimes such as 'a must not be treated as char literals. + if i + 1 < len(line) and ( + line[i + 1].isalpha() or line[i + 1] == "_" + ): + result.append("'") + i += 1 + continue + + state["char"] = True + result.append(" ") + i += 1 + continue + + result.append(line[i]) + i += 1 + + return "".join(result) + + +def is_function_start(text): + stripped = text.lstrip() + + while stripped.startswith("#["): + return False + + prefixes = ( + "fn ", + "pub fn ", + "pub(crate) fn ", + "pub(super) fn ", + "pub(self) fn ", + "async fn ", + "pub async fn ", + "pub(crate) async fn ", + "pub(super) async fn ", + "unsafe fn ", + "pub unsafe fn ", + "const fn ", + "pub const fn ", + "extern ", + "pub extern ", + ) + + return stripped.startswith(prefixes) + + +def function_ranges(lines): + state = { + "block_comment": 0, + "string": False, + "char": False, + "escape": False, + } + + depth = 0 + active = None + ranges = [] + + for index, line in enumerate(lines): + lexical = strip_lexical_content(line, state) + depth_before = depth + + if active is None and is_function_start(lexical): + active = { + "start": index, + "parent_depth": depth_before, + "body_seen": False, + } + + opens = lexical.count("{") + closes = lexical.count("}") + + # A trait/extern declaration has no body. Do not carry it forward into + # the next impl block and mistake that block's opening for its end. + if ( + active is not None + and not active["body_seen"] + and opens == 0 + and lexical.rstrip().endswith(";") + ): + active = None + + if active is not None and opens > 0: + active["body_seen"] = True + + depth += opens + depth -= closes + + if ( + active is not None + and active["body_seen"] + and depth == active["parent_depth"] + ): + ranges.append((active["start"], index, active["parent_depth"])) + active = None + + return ranges + + +def add_function_spacing(text): + lines = text.splitlines(keepends=True) + ranges = function_ranges(lines) + + if not ranges: + return text + + insert_after = set() + + for (_, end, parent_depth), (next_start, _, next_parent_depth) in zip( + ranges, + ranges[1:], + ): + if parent_depth != next_parent_depth: + continue + + between = lines[end + 1:next_start] + + if any(line.strip() for line in between): + continue + + blank_count = sum(1 for line in between if not line.strip()) + + if blank_count == 0: + insert_after.add(end) + + output = [] + + for index, line in enumerate(lines): + output.append(line) + + if index in insert_after: + output.append("\n") + + return "".join(output) + + +def format_file(path, check): + original = path.read_text(encoding="utf-8") + formatted = add_function_spacing(original) + + if formatted == original: + return False + + if check: + print(path.relative_to(ROOT)) + return True + + path.write_text(formatted, encoding="utf-8") + print(f"formatted {path.relative_to(ROOT)}") + return True + + +def run_rustfmt(check): + command = ["cargo", "fmt", "--all"] + + if check: + command.extend(["--", "--check"]) + + subprocess.run(command, cwd=ROOT, check=True) + + +def main(): + parser = argparse.ArgumentParser( + description="Format Wave Rust sources and enforce blank lines between functions.", + ) + parser.add_argument( + "--check", + action="store_true", + help="Check formatting without modifying files.", + ) + args = parser.parse_args() + + run_rustfmt(args.check) + + changed = False + + for path in rust_files(): + changed |= format_file(path, args.check) + + if args.check and changed: + print( + "Rust function spacing does not match the Wave style.", + file=sys.stderr, + ) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/utils/src/colorex.rs b/utils/src/colorex.rs index 205acd06..29e9d0b1 100644 --- a/utils/src/colorex.rs +++ b/utils/src/colorex.rs @@ -100,11 +100,8 @@ impl Colorize for &str { return self.to_string(); } - let color = if color.starts_with('#') { - Color::from_hex(color) - } else { - Color::from_rgb(color) - }; + let color = + if color.starts_with('#') { Color::from_hex(color) } else { Color::from_rgb(color) }; match color { Ok(c) => format!("\x1b[38;2;{};{};{}m{}\x1b[0m", c.0, c.1, c.2, self), @@ -117,11 +114,8 @@ impl Colorize for &str { return self.to_string(); } - let color = if color.starts_with('#') { - Color::from_hex(color) - } else { - Color::from_rgb(color) - }; + let color = + if color.starts_with('#') { Color::from_hex(color) } else { Color::from_rgb(color) }; match color { Ok(c) => format!("\x1b[48;2;{};{};{}m{}\x1b[0m", c.0, c.1, c.2, self), diff --git a/utils/src/const_int.rs b/utils/src/const_int.rs index bc788430..2aa9f44d 100644 --- a/utils/src/const_int.rs +++ b/utils/src/const_int.rs @@ -18,10 +18,7 @@ pub struct ConstInt { impl ConstInt { pub fn zero() -> Self { - Self { - negative: false, - words: Box::new([0; WORDS]), - } + Self { negative: false, words: Box::new([0; WORDS]) } } pub fn from_u64(value: u64) -> Self { @@ -57,6 +54,7 @@ impl ConstInt { pub fn is_zero(&self) -> bool { self.words.iter().all(|&w| w == 0) } + pub fn is_negative(&self) -> bool { self.negative } @@ -70,26 +68,32 @@ impl ConstInt { } words } + pub fn bits(&self) -> usize { self.words .iter() .rposition(|&w| w != 0) .map_or(0, |i| i * 64 + 64 - self.words[i].leading_zeros() as usize) } + fn bit(&self, index: usize) -> bool { index < CAPACITY && self.words[index / 64] & (1 << (index % 64)) != 0 } + fn canonical(mut self) -> Self { self.negative &= !self.is_zero(); self } + pub fn negated(mut self) -> Self { self.negative = !self.negative && !self.is_zero(); self } + fn magnitude_cmp(&self, other: &Self) -> Ordering { self.words.iter().rev().cmp(other.words.iter().rev()) } + fn magnitude_add(&self, other: &Self) -> Option { let mut result = Self::zero(); let mut carry = 0u128; @@ -100,6 +104,7 @@ impl ConstInt { } (carry == 0).then_some(result) } + fn magnitude_sub(&self, other: &Self) -> Self { debug_assert!(self.magnitude_cmp(other) != Ordering::Less); let mut result = Self::zero(); @@ -113,6 +118,7 @@ impl ConstInt { debug_assert!(!borrow); result } + pub fn checked_add(&self, other: &Self) -> Option { let (mut result, negative) = if self.negative == other.negative { (self.magnitude_add(other)?, self.negative) @@ -124,9 +130,11 @@ impl ConstInt { result.negative = negative; Some(result.canonical()) } + pub fn checked_sub(&self, other: &Self) -> Option { self.checked_add(&other.clone().negated()) } + pub fn checked_mul(&self, other: &Self) -> Option { let mut result = Self::zero(); let right_words = other.bits().div_ceil(64); @@ -177,6 +185,7 @@ impl ConstInt { remainder.negative = self.negative; Some((quotient.canonical(), remainder.canonical())) } + pub fn checked_shl(&self, shift: usize) -> Option { if self.is_zero() { return Some(Self::zero()); @@ -195,6 +204,7 @@ impl ConstInt { result.negative = self.negative; Some(result) } + fn magnitude_shr(&self, shift: usize) -> Self { let mut result = Self::zero(); if shift >= CAPACITY { @@ -218,6 +228,7 @@ impl ConstInt { result.negative = self.negative; result.canonical() } + fn twos_complement(&self) -> [u64; WORDS + 1] { let mut words = [0; WORDS + 1]; words[..WORDS].copy_from_slice(self.words.as_ref()); @@ -226,6 +237,7 @@ impl ConstInt { } words } + fn from_twos_complement(mut words: [u64; WORDS + 1]) -> Option { let negative = words[WORDS] >> 63 != 0; if negative { @@ -239,6 +251,7 @@ impl ConstInt { result.negative = negative; Some(result.canonical()) } + fn bitwise(&self, other: &Self, op: impl Fn(u64, u64) -> u64) -> Option { let mut words = self.twos_complement(); for (a, b) in words.iter_mut().zip(other.twos_complement()) { @@ -246,15 +259,19 @@ impl ConstInt { } Self::from_twos_complement(words) } + pub fn bitand(&self, other: &Self) -> Option { self.bitwise(other, |a, b| a & b) } + pub fn bitor(&self, other: &Self) -> Option { self.bitwise(other, |a, b| a | b) } + pub fn bitxor(&self, other: &Self) -> Option { self.bitwise(other, |a, b| a ^ b) } + pub fn checked_not(&self) -> Option { Self::from_twos_complement(self.twos_complement().map(|w| !w)) } @@ -274,9 +291,11 @@ impl ConstInt { words[bits.div_ceil(64)..].fill(fill); Self::from_twos_complement(words).unwrap() } + pub fn fits(&self, bits: u16, signed: bool) -> bool { self == &self.normalize(bits, signed) } + pub fn to_usize(&self) -> Option { if self.negative || self.bits() > usize::BITS as usize { return None; @@ -303,6 +322,7 @@ impl ConstInt { result.negative = raw >> 63 != 0; Some(result.canonical()) } + fn rounded_significand(&self, precision: usize) -> (u64, i32) { let shift = self.bits().saturating_sub(precision); let mut significand = self.magnitude_shr(shift).words[0]; @@ -315,6 +335,7 @@ impl ConstInt { } (significand, shift as i32) } + pub fn to_f64(&self) -> f64 { let (significand, shift) = self.rounded_significand(53); let value = significand as f64 * 2f64.powi(shift); @@ -324,6 +345,7 @@ impl ConstInt { value } } + pub fn to_f32(&self) -> f32 { let (significand, shift) = self.rounded_significand(24); let value = significand as f32 * 2f32.powi(shift); @@ -390,11 +412,7 @@ mod tests { for shift in [0, 1, 7, 63, 64, 127, 1024, usize::MAX] { assert_eq!( x.shifted_right(shift), - int(if shift < 128 { - a >> shift - } else { - -((a < 0) as i128) - }) + int(if shift < 128 { a >> shift } else { -((a < 0) as i128) }) ); } for b in -32i128..=32 { @@ -424,10 +442,7 @@ mod tests { value } } - for (index, line) in include_str!("../tests/fixtures/const_int.tsv") - .lines() - .enumerate() - { + for (index, line) in include_str!("../tests/fixtures/const_int.tsv").lines().enumerate() { if line.starts_with('#') { continue; } @@ -479,16 +494,7 @@ mod tests { } #[test] fn floating_conversions_round_once_and_decode_exactly() { - for value in [ - i128::MIN, - i128::MAX, - -16777219, - -1, - 0, - 1, - 16777217, - 16777219, - ] { + for value in [i128::MIN, i128::MAX, -16777219, -1, 0, 1, 16777217, 16777219] { assert_eq!(int(value).to_f64(), value as f64); assert_eq!(int(value).to_f32(), value as f32); } @@ -501,14 +507,8 @@ mod tests { "340282356779733661637539395458142568448", ] { let n = ConstInt::from_digits(digits, 10).unwrap(); - assert_eq!( - n.to_f64().to_bits(), - digits.parse::().unwrap().to_bits() - ); - assert_eq!( - n.to_f32().to_bits(), - digits.parse::().unwrap().to_bits() - ); + assert_eq!(n.to_f64().to_bits(), digits.parse::().unwrap().to_bits()); + assert_eq!(n.to_f32().to_bits(), digits.parse::().unwrap().to_bits()); assert_eq!(n.clone().negated().to_f32(), -n.to_f32()); } for value in [ diff --git a/utils/src/display_width.rs b/utils/src/display_width.rs index 49c8d864..98a7bf62 100644 --- a/utils/src/display_width.rs +++ b/utils/src/display_width.rs @@ -42,11 +42,7 @@ pub fn diagnostic_line(source: &str, column: usize, length: usize) -> Diagnostic let mut caret_offset = 0; let mut caret_width = 0; for (index, c) in source.chars().enumerate() { - let width = if c == '\t' { - TAB_STOP - cells % TAB_STOP - } else { - cell_width(c) - }; + let width = if c == '\t' { TAB_STOP - cells % TAB_STOP } else { cell_width(c) }; if c == '\t' { text.push_str(&" ".repeat(width)); } else { @@ -60,11 +56,7 @@ pub fn diagnostic_line(source: &str, column: usize, length: usize) -> Diagnostic } cells += width; } - DiagnosticLine { - text, - caret_offset, - caret_width: caret_width.max(1), - } + DiagnosticLine { text, caret_offset, caret_width: caret_width.max(1) } } #[cfg(test)] diff --git a/utils/src/formatx.rs b/utils/src/formatx.rs index f94c5b36..c0d9269f 100644 --- a/utils/src/formatx.rs +++ b/utils/src/formatx.rs @@ -69,10 +69,7 @@ pub fn parse_placeholders(input: &str) -> Vec { pub fn count_placeholders(input: impl AsRef<[u8]>) -> usize { format_fragments(input.as_ref()) .map(|parts| { - parts - .into_iter() - .filter(|p| matches!(p, FormatFragment::Placeholder(_))) - .count() + parts.into_iter().filter(|p| matches!(p, FormatFragment::Placeholder(_))).count() }) .unwrap_or(0) } diff --git a/utils/src/paths.rs b/utils/src/paths.rs index 57332e12..0f24b342 100644 --- a/utils/src/paths.rs +++ b/utils/src/paths.rs @@ -32,13 +32,7 @@ fn home_dir_from(windows: bool, mut get: impl FnMut(&str) -> Option) - let mut nonempty = |name| get(name).filter(|value| !value.is_empty()); // HOME also provides the explicit override used by staged std installation. nonempty("HOME") - .or_else(|| { - if windows { - nonempty("USERPROFILE") - } else { - None - } - }) + .or_else(|| if windows { nonempty("USERPROFILE") } else { None }) .map(PathBuf::from) } @@ -72,9 +66,7 @@ mod tests { #[test] fn unix_does_not_fall_back_to_a_windows_profile() { - let path = home_dir_from(false, |key| { - (key == "USERPROFILE").then(|| "/users/wave".into()) - }); + let path = home_dir_from(false, |key| (key == "USERPROFILE").then(|| "/users/wave".into())); assert_eq!(path, None); assert_eq!(home_dir_from(true, |_| None), None); assert_eq!(home_dir_from(true, |_| Some(OsString::new())), None); diff --git a/utils/src/wson/mod.rs b/utils/src/wson/mod.rs index fe784a6e..12678b99 100644 --- a/utils/src/wson/mod.rs +++ b/utils/src/wson/mod.rs @@ -37,9 +37,11 @@ impl Number { pub fn as_str(&self) -> &str { &self.raw } + pub fn as_integer(&self) -> Option<&ConstInt> { self.integer.as_ref() } + pub fn as_u64(&self) -> Option { self.raw.parse().ok() } @@ -68,51 +70,58 @@ impl Value { pub fn object>(fields: impl IntoIterator) -> Self { Self::Object(fields.into_iter().map(|(k, v)| (k.into(), v)).collect()) } + pub fn string(value: impl AsRef) -> Self { Self::String(value.as_ref().into()) } + pub fn strings>(values: impl IntoIterator) -> Self { Self::Array(values.into_iter().map(Self::string).collect()) } + pub fn optional_string(value: Option>) -> Self { value.map(Self::string).unwrap_or(Self::Null) } + pub fn get(&self, key: &str) -> Option<&Self> { match self { Self::Object(fields) => fields.iter().find(|(k, _)| k == key).map(|(_, v)| v), _ => None, } } + pub fn get_str(&self, key: &str) -> Option<&str> { match self.get(key) { Some(Self::String(s)) => Some(s), _ => None, } } + pub fn get_num(&self, key: &str) -> Option { match self.get(key) { Some(Self::Number(n)) => n.as_f64(), _ => None, } } + pub fn get_u64(&self, key: &str) -> Option { match self.get(key) { Some(Self::Number(n)) => n.as_u64(), _ => None, } } + pub fn get_arr(&self, key: &str) -> Option<&[Self]> { match self.get(key) { Some(Self::Array(a)) => Some(a), _ => None, } } + pub fn integer(value: u64) -> Self { - Self::Number(Number { - raw: value.to_string(), - integer: Some(ConstInt::from_u64(value)), - }) + Self::Number(Number { raw: value.to_string(), integer: Some(ConstInt::from_u64(value)) }) } + pub fn write_to( &self, mut output: impl Write, @@ -121,9 +130,7 @@ impl Value { ) -> Result<(), Error> { // Validate completely before producing bytes on the caller's stream. let encoded = dumps(self, format, pretty)?; - output - .write_all(encoded.as_bytes()) - .map_err(|e| Error::at(&encoded, 0, e.to_string())) + output.write_all(encoded.as_bytes()).map_err(|e| Error::at(&encoded, 0, e.to_string())) } } @@ -144,22 +151,17 @@ impl Error { '\r' => { line += 1; column = 1; - } - '\n' if after_cr => {} + }, + '\n' if after_cr => {}, '\n' => { line += 1; column = 1; - } + }, _ => column += 1, } after_cr = ch == '\r'; } - Self { - message: message.into(), - offset, - line, - column, - } + Self { message: message.into(), offset, line, column } } } impl fmt::Display for Error { @@ -176,15 +178,19 @@ impl std::error::Error for Error {} pub fn parse(input: &str, format: Format) -> Result { parser::parse(input, format) } + pub fn parse_json(input: &str) -> Result { parse(input, Format::Json) } + pub fn loads(input: &str) -> Result { parse(input, Format::Wson) } + pub fn validate(input: &str, format: Format) -> Result<(), Error> { parse(input, format).map(|_| ()) } + pub fn dumps(value: &Value, format: Format, pretty: bool) -> Result { writer::serialize(value, format, pretty, MAX_DEPTH) } @@ -209,28 +215,17 @@ fn number(raw: &str) -> Result { let digits = raw.strip_prefix('-').unwrap_or(raw); let value = ConstInt::from_digits(digits, 10) .ok_or("integer exceeds supported 2048-bit capacity")?; - Some(if raw.starts_with('-') { - value.negated() - } else { - value - }) + Some(if raw.starts_with('-') { value.negated() } else { value }) } else { let value = raw.parse::().map_err(|_| "invalid decimal")?; - let nonzero_mantissa = raw - .split(['e', 'E']) - .next() - .unwrap_or(raw) - .bytes() - .any(|b| matches!(b, b'1'..=b'9')); + let nonzero_mantissa = + raw.split(['e', 'E']).next().unwrap_or(raw).bytes().any(|b| matches!(b, b'1'..=b'9')); if !value.is_finite() || (value == 0.0 && nonzero_mantissa) { return Err("number is outside the supported finite f64 range".into()); } None }; - Ok(Value::Number(Number { - raw: raw.into(), - integer, - })) + Ok(Value::Number(Number { raw: raw.into(), integer })) } fn valid_date(raw: &str) -> bool { @@ -238,10 +233,7 @@ fn valid_date(raw: &str) -> bool { if b.len() != 10 || b[4] != b'-' || b[7] != b'-' - || !b - .iter() - .enumerate() - .all(|(i, c)| i == 4 || i == 7 || c.is_ascii_digit()) + || !b.iter().enumerate().all(|(i, c)| i == 4 || i == 7 || c.is_ascii_digit()) { return false; } @@ -249,21 +241,7 @@ fn valid_date(raw: &str) -> bool { let month: usize = raw[5..7].parse().unwrap(); let day: u32 = raw[8..].parse().unwrap(); let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); - let days = [ - 0, - 31, - 28 + u32::from(leap), - 31, - 30, - 31, - 30, - 31, - 31, - 30, - 31, - 30, - 31, - ]; + let days = [0, 31, 28 + u32::from(leap), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; (1..=12).contains(&month) && day > 0 && day <= days[month] } fn valid_datetime(raw: &str) -> bool { @@ -272,10 +250,7 @@ fn valid_datetime(raw: &str) -> bool { || b[10] != b' ' || b[13] != b':' || b[16] != b':' - || !b[11..] - .iter() - .enumerate() - .all(|(i, c)| i == 2 || i == 5 || c.is_ascii_digit()) + || !b[11..].iter().enumerate().all(|(i, c)| i == 2 || i == 5 || c.is_ascii_digit()) || !raw.is_char_boundary(10) || !valid_date(&raw[..10]) { diff --git a/utils/src/wson/parser.rs b/utils/src/wson/parser.rs index 78b85f29..0392ad55 100644 --- a/utils/src/wson/parser.rs +++ b/utils/src/wson/parser.rs @@ -22,12 +22,7 @@ struct Parser<'a> { impl<'a> Parser<'a> { fn new(s: &'a [u8], format: Format) -> Self { - Self { - s, - i: 0, - depth: 0, - format, - } + Self { s, i: 0, depth: 0, format } } fn eof(&self) -> bool { @@ -91,15 +86,15 @@ impl<'a> Parser<'a> { b'n' => { self.consume_bytes(b"null")?; Ok(Value::Null) - } + }, b't' => { self.consume_bytes(b"true")?; Ok(Value::Bool(true)) - } + }, b'f' => { self.consume_bytes(b"false")?; Ok(Value::Bool(false)) - } + }, b'"' => Ok(Value::String(self.parse_string()?)), b'[' => Ok(Value::Array(self.parse_array()?)), b'{' => Ok(Value::Object(self.parse_object()?)), @@ -150,16 +145,16 @@ impl<'a> Parser<'a> { return Err("invalid low surrogate".into()); } 0x10000 + ((first - 0xd800) << 10) + (low - 0xdc00) - } + }, 0xdc00..=0xdfff => return Err("lone low surrogate".into()), other => other, }; char::from_u32(scalar).ok_or("invalid Unicode scalar")? - } + }, _ => return Err("invalid escape".into()), }; out.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()); - } + }, 0..=0x1f => return Err("unescaped control byte in string".into()), _ => out.push(c), } @@ -195,7 +190,7 @@ impl<'a> Parser<'a> { while matches!(self.peek(), Some(b'0'..=b'9')) { self.i += 1; } - } + }, _ => return Err("invalid number".into()), } @@ -240,9 +235,7 @@ impl<'a> Parser<'a> { } self.i += 1; } - let raw = std::str::from_utf8(&self.s[start..self.i]) - .map_err(|_| "invalid UTF-8")? - .trim(); + let raw = std::str::from_utf8(&self.s[start..self.i]).map_err(|_| "invalid UTF-8")?.trim(); if raw.eq_ignore_ascii_case("null") { return Ok(Value::Null); } @@ -260,16 +253,11 @@ impl<'a> Parser<'a> { } let parts: Vec<_> = raw.split('.').collect(); if parts.len() >= 3 - && parts - .iter() - .all(|p| !p.is_empty() && p.bytes().all(|b| b.is_ascii_digit())) + && parts.iter().all(|p| !p.is_empty() && p.bytes().all(|b| b.is_ascii_digit())) { return parts .iter() - .map(|s| { - s.parse::() - .map_err(|_| "version component exceeds u32".into()) - }) + .map(|s| s.parse::().map_err(|_| "version component exceeds u32".into())) .collect::, String>>() .map(Value::Version); } @@ -312,7 +300,7 @@ impl<'a> Parser<'a> { break; } continue; - } + }, b']' => break, _ => return Err("expected ',' or ']'".into()), } @@ -353,9 +341,7 @@ impl<'a> Parser<'a> { if self.i == start { return Err("object key must be a string or identifier".into()); } - std::str::from_utf8(&self.s[start..self.i]) - .unwrap() - .to_owned() + std::str::from_utf8(&self.s[start..self.i]).unwrap().to_owned() } else { return Err("object key must be string".into()); }; @@ -379,7 +365,7 @@ impl<'a> Parser<'a> { break; } continue; - } + }, b'}' => break, _ => return Err("expected ',' or '}'".into()), } diff --git a/utils/src/wson/tests.rs b/utils/src/wson/tests.rs index 7a7c6874..a96a51b6 100644 --- a/utils/src/wson/tests.rs +++ b/utils/src/wson/tests.rs @@ -14,14 +14,9 @@ fn wson_comments_dates_versions_and_order_share_the_value_model() { assert_eq!(value.get_str("z"), Some("https://wave/#anchor /* text */")); assert_eq!(value.get_num("a"), Some(1.5)); assert_eq!(value.get("version"), Some(&Value::Version(vec![1, 2, 3]))); - assert_eq!( - loads(&dumps(&value, Format::Wson, true).unwrap()).unwrap(), - value - ); + assert_eq!(loads(&dumps(&value, Format::Wson, true).unwrap()).unwrap(), value); assert!(dumps(&value, Format::Json, false).is_err()); - let Value::Object(fields) = value else { - panic!("object") - }; + let Value::Object(fields) = value else { panic!("object") }; assert_eq!(fields[0].0, "z"); for bad in [ "{a=2023-02-29}", @@ -38,13 +33,7 @@ fn wson_comments_dates_versions_and_order_share_the_value_model() { #[test] fn exact_numbers_never_round_through_f64() { let integer = "17976931348623159077293051907890247336179769789423065727343008115"; - for text in [ - integer, - "18446744073709551615", - "-0", - "-0.000e+10", - "1.230000e-12", - ] { + for text in [integer, "18446744073709551615", "-0", "-0.000e+10", "1.230000e-12"] { let value = parse_json(text).unwrap(); assert_eq!(dumps(&value, Format::Json, false).unwrap(), text); } @@ -58,14 +47,8 @@ fn exact_numbers_never_round_through_f64() { #[test] fn duplicate_keys_and_depth_are_rejected_on_both_read_and_write() { - for (text, format) in [ - (r#"{"x":1,"\u0078":2}"#, Format::Json), - ("{x=1,x=2}", Format::Wson), - ] { - assert!(parse(text, format) - .unwrap_err() - .message - .contains("duplicate")); + for (text, format) in [(r#"{"x":1,"\u0078":2}"#, Format::Json), ("{x=1,x=2}", Format::Wson)] { + assert!(parse(text, format).unwrap_err().message.contains("duplicate")); } assert!(dumps( &Value::Object(vec![("x".into(), Value::Null), ("x".into(), Value::Null)]), @@ -79,26 +62,15 @@ fn duplicate_keys_and_depth_are_rejected_on_both_read_and_write() { assert!(dumps(&value, format, false).is_ok()); assert!(parse(&format!("[{at_limit}]"), format).is_err()); let mut output = Vec::new(); - assert!(Value::Array(vec![value]) - .write_to(&mut output, format, false) - .is_err()); + assert!(Value::Array(vec![value]).write_to(&mut output, format, false).is_err()); assert!(output.is_empty()); } } #[test] fn json_mode_is_strict_and_errors_keep_physical_positions() { - for text in [ - "{x:1}", - "{\"x\"=1}", - "[1,]", - "{/*comment*/}", - "True", - "01", - "+1", - "NaN", - "1.2.3", - ] { + for text in ["{x:1}", "{\"x\"=1}", "[1,]", "{/*comment*/}", "True", "01", "+1", "NaN", "1.2.3"] + { assert!(parse_json(text).is_err(), "{text}"); } let text = "{\r\n\"한\": 1,\r\"한\":2}"; @@ -148,10 +120,7 @@ fn all_raw_control_bytes_are_rejected_in_values_and_keys() { for byte in 0..=31u8 { let text = format!("\"{}\"", char::from(byte)); assert!(parse_json(&text).is_err(), "byte {byte}"); - assert!( - parse_json(&format!("{{{text}:0}}")).is_err(), - "key byte {byte}" - ); + assert!(parse_json(&format!("{{{text}:0}}")).is_err(), "key byte {byte}"); assert!(parse_json(&format!(r#""\u{byte:04x}""#)).is_ok()); } } @@ -159,18 +128,9 @@ fn all_raw_control_bytes_are_rejected_in_values_and_keys() { #[test] fn numeric_range_errors_do_not_change_json_types() { for text in ["1e9999", "-1e9999", "1.7976931348623159e308"] { - assert!(parse_json(text) - .unwrap_err() - .message - .contains("finite f64 range")); + assert!(parse_json(text).unwrap_err().message.contains("finite f64 range")); } - for text in [ - "1.7976931348623157e308", - "-1.7976931348623157e308", - "1.5", - "-0", - "5e-324", - ] { + for text in ["1.7976931348623157e308", "-1.7976931348623157e308", "1.5", "-0", "5e-324"] { let value = parse_json(text).unwrap(); let output = dumps(&value, Format::Json, false).unwrap(); assert_eq!(output, text); diff --git a/utils/src/wson/writer.rs b/utils/src/wson/writer.rs index 72bb85a2..a8901561 100644 --- a/utils/src/wson/writer.rs +++ b/utils/src/wson/writer.rs @@ -16,7 +16,7 @@ pub fn quote(value: &str) -> String { '\x0c' => out.push_str("\\f"), c if c < '\x20' => { write!(out, "\\u{:04x}", c as u32).unwrap(); - } + }, c => out.push(c), } } @@ -34,15 +34,18 @@ pub(super) fn serialize( emit(&mut out, value, format, pretty, 0, max_depth)?; Ok(out) } + fn fail(out: &str, message: &str) -> Error { Error::at(out, out.len(), message) } + fn newline(out: &mut String, pretty: bool, depth: usize) { if pretty { out.push('\n'); out.push_str(&" ".repeat(depth)); } } + fn emit( out: &mut String, value: &Value, @@ -63,34 +66,22 @@ fn emit( if format == Format::Json { return Err(fail(out, "WSON date requires explicit conversion for JSON")); } - let valid = if matches!(value, Value::Date(_)) { - valid_date(s) - } else { - valid_datetime(s) - }; + let valid = + if matches!(value, Value::Date(_)) { valid_date(s) } else { valid_datetime(s) }; if !valid { return Err(fail(out, "invalid date or datetime")); } out.push_str(s); - } + }, Value::Version(parts) => { if format == Format::Json { - return Err(fail( - out, - "WSON version requires explicit conversion for JSON", - )); + return Err(fail(out, "WSON version requires explicit conversion for JSON")); } if parts.len() < 3 { return Err(fail(out, "version requires at least three components")); } - out.push_str( - &parts - .iter() - .map(u32::to_string) - .collect::>() - .join("."), - ); - } + out.push_str(&parts.iter().map(u32::to_string).collect::>().join(".")); + }, Value::Array(values) => { out.push('['); for (i, v) in values.iter().enumerate() { @@ -104,7 +95,7 @@ fn emit( newline(out, pretty, depth); } out.push(']'); - } + }, Value::Object(fields) => { let mut keys = std::collections::HashSet::new(); out.push('{'); @@ -128,7 +119,7 @@ fn emit( newline(out, pretty, depth); } out.push('}'); - } + }, } Ok(()) } diff --git a/whale/src/codegen/expression.rs b/whale/src/codegen/expression.rs index 823b5b84..44daf1f7 100644 --- a/whale/src/codegen/expression.rs +++ b/whale/src/codegen/expression.rs @@ -22,6 +22,7 @@ impl Lowerer<'_> { self.expr(e).map(|_| ()) } } + pub(super) fn expr(&mut self, e: &Expression) -> Result { self.expression(e).map_err(|mut error| { if error.span.is_none() { @@ -35,6 +36,7 @@ impl Lowerer<'_> { error }) } + pub(super) fn unary_value( &mut self, operator: &Operator, @@ -47,11 +49,11 @@ impl Lowerer<'_> { // Multiplication by -1 preserves both signs of zero. let minus_one = self.literal(&Literal::Float(-1.0), &fact.evaluation_type)?; self.bin(BinOp::FMul, ty.clone(), operand, minus_one)? - } + }, Operator::Neg => { let zero = self.literal(&Literal::Int("0".into()), &fact.evaluation_type)?; self.bin(BinOp::Sub, ty.clone(), zero, operand)? - } + }, Operator::LogicalNot | Operator::Not if ty == Type::Bool => { let input = fact .computation_type @@ -67,46 +69,36 @@ impl Lowerer<'_> { let zero = self.literal(&Literal::Int("0".into()), input)?; self.icmp(ICmpPred::Eq, operand, zero)? } - } + }, Operator::BitwiseNot => { let value = self.value(ty.clone()); - self.emit(Instruction::Not { - dst: value.id, - ty: ty.clone(), - src: operand.id, - }); + self.emit(Instruction::Not { dst: value.id, ty: ty.clone(), src: operand.id }); value - } + }, _ => return Err(unsupported("this unary operation")), }) } - fn constant(&mut self, ty: Type, literal: ConstValue) -> Value { + + pub(super) fn constant(&mut self, ty: Type, literal: ConstValue) -> Value { let value = self.value(ty.clone()); - self.emit(Instruction::Const { - dst: value.id, - ty, - value: literal, - }); + self.emit(Instruction::Const { dst: value.id, ty, value: literal }); value } + pub(super) fn literal(&mut self, literal: &Literal, ty: &WaveType) -> Result { let ir_ty = scalar(ty)?; let constant = match literal { Literal::Bool(v) if *ty == WaveType::Bool => ConstValue::Bool(*v), Literal::Float(v) if matches!(ty, WaveType::Float(_)) => { - let WaveType::Float(bits) = ty else { - unreachable!() - }; + let WaveType::Float(bits) = ty else { unreachable!() }; ConstValue::F(FloatBits::from_f64(*bits, *v).map_err(ice)?) - } + }, Literal::Int(raw) if matches!(ty, WaveType::Float(_)) => { - let WaveType::Float(bits) = ty else { - unreachable!() - }; + let WaveType::Float(bits) = ty else { unreachable!() }; let n = hir::integer_literal_float(raw, *bits) .ok_or_else(|| ice("invalid typed floating literal"))?; ConstValue::F(FloatBits::from_f64(*bits, n).map_err(ice)?) - } + }, Literal::Int(raw) => { let token = lexer::number::IntegerLiteral::parse(raw) .ok_or_else(|| ice("invalid integer token"))?; @@ -124,11 +116,12 @@ impl Lowerer<'_> { } else { ConstValue::I(raw as i128) } - } + }, _ => return Err(unsupported("this literal")), }; Ok(self.constant(ir_ty, constant)) } + pub(super) fn convert(&mut self, input: Value, conversion: &ConversionInfo) -> Result { let src_ty = scalar(&conversion.source_type)?; let dst_ty = scalar(&conversion.target_type)?; @@ -148,25 +141,21 @@ impl Lowerer<'_> { ConversionKind::IntegerToBool => { let zero = self.literal(&Literal::Int("0".into()), &conversion.source_type)?; return self.icmp(ICmpPred::Ne, input, zero); - } + }, ConversionKind::FloatToBool => { let zero = self.literal(&Literal::Float(0.0), &conversion.source_type)?; return self.fcmp(FCmpPred::Une, input, zero); - } - // Raw Whale FToI instructions do not implement Wave's checked range - // contract. Reject until the adapter emits those checks explicitly. + }, + ConversionKind::FloatToSigned | ConversionKind::FloatToUnsigned => { + return self.checked_float_to_integer(input, conversion); + }, _ => return Err(unsupported(format!("conversion {:?}", conversion.kind))), }; let value = self.value(dst_ty.clone()); - self.emit(Instruction::Cast { - dst: value.id, - op, - dst_ty, - src_ty, - src: input.id, - }); + self.emit(Instruction::Cast { dst: value.id, op, dst_ty, src_ty, src: input.id }); Ok(value) } + fn call(&mut self, name: &str, args: &[Expression]) -> Result> { let mut values = Vec::with_capacity(args.len()); for arg in args { @@ -174,6 +163,7 @@ impl Lowerer<'_> { } self.call_values(name, values) } + pub(super) fn call_values(&mut self, name: &str, args: Vec) -> Result> { let (id, signature) = self .functions @@ -196,50 +186,34 @@ impl Lowerer<'_> { }); Ok(value) } - fn bin(&mut self, op: BinOp, ty: Type, lhs: Value, rhs: Value) -> Result { + + pub(super) fn bin(&mut self, op: BinOp, ty: Type, lhs: Value, rhs: Value) -> Result { if lhs.ty != ty || rhs.ty != ty { - return Err(ice( - "arithmetic operands disagree with HIR computation type", - )); + return Err(ice("arithmetic operands disagree with HIR computation type")); } let value = self.value(ty.clone()); - self.emit(Instruction::Bin { - dst: value.id, - op, - ty, - lhs: lhs.id, - rhs: rhs.id, - }); + self.emit(Instruction::Bin { dst: value.id, op, ty, lhs: lhs.id, rhs: rhs.id }); Ok(value) } - fn icmp(&mut self, pred: ICmpPred, lhs: Value, rhs: Value) -> Result { + + pub(super) fn icmp(&mut self, pred: ICmpPred, lhs: Value, rhs: Value) -> Result { if lhs.ty != rhs.ty { return Err(ice("comparison operand type mismatch")); } let value = self.value(Type::Bool); - self.emit(Instruction::ICmp { - dst: value.id, - pred, - ty: lhs.ty, - lhs: lhs.id, - rhs: rhs.id, - }); + self.emit(Instruction::ICmp { dst: value.id, pred, ty: lhs.ty, lhs: lhs.id, rhs: rhs.id }); Ok(value) } - fn fcmp(&mut self, pred: FCmpPred, lhs: Value, rhs: Value) -> Result { + + pub(super) fn fcmp(&mut self, pred: FCmpPred, lhs: Value, rhs: Value) -> Result { if lhs.ty != rhs.ty { return Err(ice("comparison operand type mismatch")); } let value = self.value(Type::Bool); - self.emit(Instruction::FCmp { - dst: value.id, - pred, - ty: lhs.ty, - lhs: lhs.id, - rhs: rhs.id, - }); + self.emit(Instruction::FCmp { dst: value.id, pred, ty: lhs.ty, lhs: lhs.id, rhs: rhs.id }); Ok(value) } + pub(super) fn binary_values( &mut self, lhs: Value, diff --git a/whale/src/codegen/mod.rs b/whale/src/codegen/mod.rs index d2d642be..c357ff8f 100644 --- a/whale/src/codegen/mod.rs +++ b/whale/src/codegen/mod.rs @@ -2,6 +2,9 @@ //! Experimental Wave typed HIR -> Whale IR adapter. No LLVM fallback or ABI //! inference belongs here. Unsupported Wave constructs fail before publication. mod expression; +mod numeric; +#[cfg(test)] +mod tests; mod walk; use error::SourceSpan; @@ -23,15 +26,14 @@ fn unsupported(message: impl Into) -> LowerError { span: None, } } + fn ice(message: impl Into) -> LowerError { LowerError { - message: format!( - "internal compiler error in Whale lowering: {}", - message.into() - ), + message: format!("internal compiler error in Whale lowering: {}", message.into()), span: None, } } + fn scalar(ty: &WaveType) -> Result { Ok(match ty { WaveType::Int(8) => Type::I8, @@ -54,10 +56,7 @@ fn scalar(ty: &WaveType) -> Result { pub(crate) fn lower(program: &TypedProgram) -> Result { program.verify_conversions().map_err(|e| LowerError { - message: format!( - "internal compiler error: invalid HIR conversions: {}", - e.message - ), + message: format!("internal compiler error: invalid HIR conversions: {}", e.message), span: e.span, })?; let mut module = Module::new("x86_64-whale-linux", DataLayout::default_64bit_le()); @@ -72,10 +71,7 @@ pub(crate) fn lower(program: &TypedProgram) -> Result { } let id = FunctionId(module.declarations.len() as u32); let signature = FunctionSignature::whale( - f.parameters - .iter() - .map(|p| scalar(&p.param_type)) - .collect::>()?, + f.parameters.iter().map(|p| scalar(&p.param_type)).collect::>()?, scalar(f.return_type.as_ref().unwrap_or(&WaveType::Void))?, ); functions.insert(f.name.clone(), (id, signature.clone())); @@ -98,9 +94,7 @@ pub(crate) fn lower(program: &TypedProgram) -> Result { })?; } for node in program.syntax() { - let ASTNode::Function(f) = node.unspanned() else { - unreachable!() - }; + let ASTNode::Function(f) = node.unspanned() else { unreachable!() }; let function = Lowerer::function(program, &functions, f).map_err(|mut e| { if e.span.is_none() { e.span = f.span.clone().or_else(|| node.span().cloned()); @@ -166,46 +160,47 @@ impl<'a> Lowerer<'a> { this.body(&source.body)?; if !this.terminated() { if this.function.ret_ty != Type::Void { - return Err(unsupported( - "implicit non-void returns; add an explicit return", - )); + return Err(unsupported("implicit non-void returns; add an explicit return")); } - this.terminate(Terminator::Ret { - ty: Type::Void, - value: None, - }); + this.terminate(Terminator::Ret { ty: Type::Void, value: None }); } Ok(this.function) } + fn value(&mut self, ty: Type) -> Value { let id = ValueId(self.function.value_types.len() as u32); self.function.value_types.push((id, ty.clone())); Value { id, ty } } + fn emit(&mut self, instruction: Instruction) { - self.function.blocks[self.block] - .instructions - .push(instruction); + self.function.blocks[self.block].instructions.push(instruction); } + fn terminate(&mut self, terminator: Terminator) { self.function.blocks[self.block].terminator = Some(terminator); } + fn terminated(&self) -> bool { self.function.blocks[self.block].is_terminated() } + fn new_block(&mut self, name: &str) -> BlockId { let id = BlockId(self.function.blocks.len() as u32); self.function.blocks.push(BasicBlock::new(id, name)); id } + fn switch(&mut self, block: BlockId) { self.block = block.0 as usize; } + fn branch_if_open(&mut self, target: BlockId) { if !self.terminated() { self.terminate(Terminator::Br { target }); } } + fn slot(&self, name: &str) -> Result { self.scopes .iter() @@ -214,16 +209,15 @@ impl<'a> Lowerer<'a> { .cloned() .ok_or_else(|| unsupported(format!("binding `{name}`"))) } + fn bind(&mut self, name: &str, ty: Type, init: Option) -> Result<()> { let ptr = self.value(Type::ptr_to(ty.clone())).id; // All local storage belongs to the entry block, including loop locals. - self.function.blocks[0] - .instructions - .push(Instruction::Alloca { - dst: ptr, - ty: ty.clone(), - align: 1, - }); + self.function.blocks[0].instructions.push(Instruction::Alloca { + dst: ptr, + ty: ty.clone(), + align: 1, + }); let slot = Slot { ptr, ty }; if let Some(value) = init { self.store(&slot, value)?; @@ -231,6 +225,7 @@ impl<'a> Lowerer<'a> { self.scopes.last_mut().unwrap().insert(name.into(), slot); Ok(()) } + fn store(&mut self, slot: &Slot, value: Value) -> Result<()> { if value.ty != slot.ty { return Err(ice("store disagrees with HIR destination type")); @@ -243,6 +238,7 @@ impl<'a> Lowerer<'a> { }); Ok(()) } + fn body(&mut self, body: &[ASTNode]) -> Result<()> { self.scopes.push(HashMap::new()); for node in body { @@ -264,6 +260,7 @@ impl<'a> Lowerer<'a> { self.scopes.pop(); Ok(()) } + fn node(&mut self, node: &ASTNode) -> Result<()> { match node.unspanned() { ASTNode::Variable(v) => { @@ -277,14 +274,14 @@ impl<'a> Lowerer<'a> { return Err(unsupported("uninitialized locals")); } self.bind(&v.name, ty, init) - } + }, ASTNode::Expression(e) | ASTNode::Statement(StatementNode::Expression(e)) => { self.effect(e) - } + }, ASTNode::Statement(StatementNode::Assign { variable, value }) => { let value = self.expr(value)?; self.store(&self.slot(variable)?, value) - } + }, ASTNode::Statement(StatementNode::Return(expr)) => { let value = expr.as_ref().map(|e| self.expr(e)).transpose()?; if value.as_ref().map(|v| &v.ty).unwrap_or(&Type::Void) != &self.function.ret_ty { @@ -295,7 +292,7 @@ impl<'a> Lowerer<'a> { value: value.map(|v| v.id), }); Ok(()) - } + }, ASTNode::Statement(StatementNode::If { condition, body, @@ -312,11 +309,7 @@ impl<'a> Lowerer<'a> { let cond = self.condition(condition)?; let then_bb = self.new_block("if.then"); let else_bb = self.new_block("if.else"); - self.terminate(Terminator::CBr { - cond, - then_bb, - else_bb, - }); + self.terminate(Terminator::CBr { cond, then_bb, else_bb }); self.switch(then_bb); self.body(body)?; falls_through |= !self.terminated(); @@ -335,7 +328,7 @@ impl<'a> Lowerer<'a> { }); } Ok(()) - } + }, ASTNode::Statement(StatementNode::While { condition, body }) => { let test = self.new_block("while.test"); let loop_body = self.new_block("while.body"); @@ -343,11 +336,7 @@ impl<'a> Lowerer<'a> { self.terminate(Terminator::Br { target: test }); self.switch(test); let cond = self.condition(condition)?; - self.terminate(Terminator::CBr { - cond, - then_bb: loop_body, - else_bb: end, - }); + self.terminate(Terminator::CBr { cond, then_bb: loop_body, else_bb: end }); self.switch(loop_body); self.loops.push((test, end)); self.body(body)?; @@ -355,24 +344,19 @@ impl<'a> Lowerer<'a> { self.branch_if_open(test); self.switch(end); Ok(()) - } + }, ASTNode::Statement(statement @ (StatementNode::Break | StatementNode::Continue)) => { - let &(test, end) = self - .loops - .last() - .ok_or_else(|| ice("loop control outside loop"))?; + let &(test, end) = + self.loops.last().ok_or_else(|| ice("loop control outside loop"))?; self.terminate(Terminator::Br { - target: if matches!(statement, StatementNode::Break) { - end - } else { - test - }, + target: if matches!(statement, StatementNode::Break) { end } else { test }, }); Ok(()) - } + }, _ => Err(unsupported("this statement")), } } + fn condition(&mut self, e: &Expression) -> Result { let value = self.expr(e)?; if value.ty != Type::Bool { diff --git a/whale/src/codegen/numeric.rs b/whale/src/codegen/numeric.rs new file mode 100644 index 00000000..9202619e --- /dev/null +++ b/whale/src/codegen/numeric.rs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: MPL-2.0 +//! Checked numeric lowering. Bounds and operand types are supplied by Typed HIR. +use super::*; +use hir::conversions::{ + float_integer_range, integer_width, unsigned, ConversionInfo, ConversionKind, +}; +use parser::ast::{Literal, Operator}; +use utils::const_int::ConstInt; + +impl Lowerer<'_> { + fn cast_value(&mut self, op: CastOp, input: Value, ty: Type) -> Value { + let value = self.value(ty.clone()); + self.emit(Instruction::Cast { + dst: value.id, + op, + src_ty: input.ty, + dst_ty: ty, + src: input.id, + }); + value + } + + fn trap_unless(&mut self, valid: Value, reason: &str) -> Result<()> { + let no = self.constant(Type::Bool, ConstValue::Bool(false)); + let invalid = self.icmp(ICmpPred::Eq, valid, no)?; + self.emit(Instruction::TrapIf { cond: invalid.id, reason: reason.into() }); + Ok(()) + } + + pub(super) fn checked_float_to_integer( + &mut self, + input: Value, + conversion: &ConversionInfo, + ) -> Result { + let range = float_integer_range(&conversion.source_type, &conversion.target_type); + // These bounds already account for truncation toward zero and rounding + // at the source float width. In particular, (-1, 0) converts to unsigned 0. + for (pred, bound) in [ + (if range.lower_inclusive { FCmpPred::Oge } else { FCmpPred::Ogt }, range.lower), + (FCmpPred::Olt, range.upper), + (FCmpPred::Ogt, f64::NEG_INFINITY), + ] { + let bound = self.literal(&Literal::Float(bound), &conversion.source_type)?; + let valid = self.fcmp(pred, input.clone(), bound)?; + self.trap_unless(valid, "float-to-integer conversion out of range")?; + } + let op = match conversion.kind { + ConversionKind::FloatToSigned => CastOp::FToI_S, + ConversionKind::FloatToUnsigned => CastOp::FToI_U, + _ => return Err(ice("non-float conversion passed to checked float lowering")), + }; + Ok(self.cast_value(op, input, scalar(&conversion.target_type)?)) + } + + pub(super) fn checked_shift( + &mut self, + lhs: Value, + operator: &Operator, + rhs: Value, + computation: &WaveType, + count_type: &WaveType, + ) -> Result { + if lhs.ty != scalar(computation)? || rhs.ty != scalar(count_type)? { + return Err(ice("shift operands disagree with HIR types")); + } + let width = integer_width(computation).ok_or_else(|| ice("non-integer shift LHS"))?; + let count_width = integer_width(count_type).ok_or_else(|| ice("non-integer shift RHS"))?; + // Sign-extend before interpreting the count as unsigned. Negative counts + // then exceed the bound, and a wide count is never truncated before testing. + let check_width = count_width.max(16); + let check_type = WaveType::Uint(check_width); + let ty = scalar(&check_type)?; + let count = if rhs.ty == ty { + rhs + } else { + let op = if count_width == check_width { + CastOp::Bitcast + } else if unsigned(count_type) { + CastOp::ZExt + } else { + CastOp::SExt + }; + self.cast_value(op, rhs, ty) + }; + let bound = self.literal(&Literal::Int(width.to_string()), &check_type)?; + let valid = self.icmp(ICmpPred::Ult, count.clone(), bound)?; + self.trap_unless(valid, "shift count out of range")?; + let count = if count.ty == lhs.ty { + count + } else { + let op = match check_width.cmp(&width) { + std::cmp::Ordering::Less => CastOp::ZExt, + std::cmp::Ordering::Equal => CastOp::Bitcast, + std::cmp::Ordering::Greater => CastOp::Trunc, + }; + self.cast_value(op, count, lhs.ty.clone()) + }; + self.shift_value(lhs, operator, count, computation) + } + + pub(super) fn wide_constant_shift( + &mut self, + lhs: Value, + operator: &Operator, + right: &Expression, + computation: &WaveType, + ) -> Result { + // HIR deliberately keeps an untyped count literal at i1024. Check that + // value with Utils before selecting its IR representation. This does not + // enable runtime integers wider than Whale's supported scalar types. + let n = if let Some(hir::ConstantValue::Int(n)) = self.program.constant_value_of(right) { + n.clone() + } else { + // Short-circuited operands need not have a cached constant. Retain + // their trap in the unreachable RHS block rather than rejecting it. + let mut expression = right; + let mut negative = false; + loop { + let fact = self + .program + .numeric_expression_of(expression) + .ok_or_else(|| ice("missing shift literal facts"))?; + if !fact.conversions.is_empty() { + return Err(unsupported("runtime shift counts wider than 128 bits")); + } + match expression.unspanned() { + Expression::Grouped(inner) => expression = inner, + Expression::Unary { operator: Operator::Neg, expr } => { + negative = !negative; + expression = expr; + }, + Expression::Literal(Literal::Int(raw)) => { + let token = lexer::number::IntegerLiteral::parse(raw) + .ok_or_else(|| ice("invalid shift literal"))?; + let n = ConstInt::from_digits(&token.digits, token.radix) + .ok_or_else(|| ice("invalid shift literal digits"))?; + break if negative ^ token.negative { n.negated() } else { n }; + }, + _ => return Err(unsupported("runtime shift counts wider than 128 bits")), + } + } + }; + let width = integer_width(computation).ok_or_else(|| ice("non-integer shift LHS"))?; + let valid = !n.is_negative() && n < ConstInt::from_u64(u64::from(width)); + let valid_value = self.constant(Type::Bool, ConstValue::Bool(valid)); + self.trap_unless(valid_value, "shift count out of range")?; + // An invalid constant can occur in a short-circuited branch. The trap + // precedes a harmless placeholder; never truncate the invalid count. + let count = + if valid { n.to_usize().ok_or_else(|| ice("bounded count does not fit"))? } else { 0 }; + let count = self.literal(&Literal::Int(count.to_string()), computation)?; + self.shift_value(lhs, operator, count, computation) + } + + fn shift_value( + &mut self, + lhs: Value, + operator: &Operator, + count: Value, + computation: &WaveType, + ) -> Result { + let op = match operator { + Operator::ShiftLeft => BinOp::Shl, + Operator::ShiftRight if unsigned(computation) => BinOp::LShr, + Operator::ShiftRight => BinOp::AShr, + _ => return Err(ice("shift facts attached to a non-shift operation")), + }; + self.bin(op, scalar(computation)?, lhs, count) + } +} diff --git a/whale/src/codegen/tests.rs b/whale/src/codegen/tests.rs new file mode 100644 index 00000000..ada22bbb --- /dev/null +++ b/whale/src/codegen/tests.rs @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: MPL-2.0 +use super::*; +use utils::const_int::ConstInt; + +fn module(source: &str) -> Module { + let tokens = lexer::Lexer::new_with_file(source, "numeric.wave").tokenize().unwrap(); + let syntax = parser::parse_syntax_with_spans(&tokens).unwrap(); + let program = TypedProgram::lower(syntax).unwrap(); + let module = lower(&program).unwrap(); + let text = print_module(&module); + let parsed = parse_module(&text).unwrap_or_else(|e| panic!("{e:?}\n{text}")); + assert_eq!(print_module(&parsed), text, "IR serialization must round-trip exactly"); + parsed +} + +fn width(ty: &Type) -> u32 { + match ty { + Type::I8 | Type::U8 => 8, + Type::I16 | Type::U16 => 16, + Type::I32 | Type::U32 => 32, + Type::I64 | Type::U64 => 64, + Type::I128 | Type::U128 => 128, + _ => panic!("not an integer: {ty}"), + } +} + +fn mask(bits: u32) -> u128 { + u128::MAX >> (128 - bits) +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Probe { + Bits(u128), + Float(f64), + Bool(bool), +} + +// Evaluate only the emitted straight-line guard prefix, stopping before the +// guarded operation. This is a test probe, not a Whale execution backend. +fn guard_accepts(function: &Function, arguments: &[Probe]) -> bool { + let mut values = HashMap::new(); + let mut slots = HashMap::new(); + assert_eq!(function.blocks.len(), 1); + assert_eq!(function.params.len(), arguments.len()); + for (parameter, argument) in function.params.iter().zip(arguments) { + values.insert(parameter.id, *argument); + } + let mut traps = 0; + for instruction in &function.blocks[0].instructions { + let (dst, value) = match instruction { + Instruction::Alloca { .. } => continue, + Instruction::Store { ptr, value, .. } => { + slots.insert(*ptr, values[value]); + continue; + }, + Instruction::Load { dst, ptr, .. } => (*dst, slots[ptr]), + Instruction::Const { dst, ty, value } => ( + *dst, + match value { + ConstValue::I(n) => Probe::Bits(*n as u128 & mask(width(ty))), + ConstValue::U(n) => Probe::Bits(*n & mask(width(ty))), + ConstValue::Bool(b) => Probe::Bool(*b), + ConstValue::F(f) => Probe::Float(f.to_f64()), + }, + ), + Instruction::Cast { op: CastOp::FToI_S | CastOp::FToI_U, .. } + | Instruction::Bin { op: BinOp::Shl | BinOp::LShr | BinOp::AShr, .. } => { + assert!(traps > 0, "guarded operation must follow a trap check"); + return true; + }, + Instruction::Cast { dst, src, src_ty, dst_ty, op } => { + let Probe::Bits(mut n) = values[src] else { panic!("integer cast expected") }; + if *op == CastOp::SExt && n & (1 << (width(src_ty) - 1)) != 0 { + n |= !mask(width(src_ty)); + } + assert!(matches!( + op, + CastOp::SExt | CastOp::ZExt | CastOp::Trunc | CastOp::Bitcast + )); + if width(dst_ty) < width(src_ty) { + assert!(traps > 0, "count narrowing must follow the range check"); + } + (*dst, Probe::Bits(n & mask(width(dst_ty)))) + }, + Instruction::ICmp { dst, pred, lhs, rhs, .. } => { + let result = match pred { + ICmpPred::Eq => values[lhs] == values[rhs], + ICmpPred::Ult => { + let (Probe::Bits(a), Probe::Bits(b)) = (values[lhs], values[rhs]) else { + panic!("integer comparison expected") + }; + a < b + }, + _ => panic!("unexpected guard predicate: {pred:?}"), + }; + (*dst, Probe::Bool(result)) + }, + Instruction::FCmp { dst, pred, lhs, rhs, .. } => { + let (Probe::Float(a), Probe::Float(b)) = (values[lhs], values[rhs]) else { + panic!("float comparison expected") + }; + let result = match pred { + FCmpPred::Ogt => a > b, + FCmpPred::Oge => a >= b, + FCmpPred::Olt => a < b, + _ => panic!("unexpected guard predicate: {pred:?}"), + }; + (*dst, Probe::Bool(result)) + }, + Instruction::TrapIf { cond, .. } => { + traps += 1; + match values[cond] { + Probe::Bool(true) => return false, + Probe::Bool(false) => continue, + _ => panic!("non-bool trap condition"), + } + }, + _ => panic!("unexpected guard instruction: {instruction:?}"), + }; + values.insert(dst, value); + } + panic!("missing guarded operation"); +} + +#[test] +fn shift_guards_check_original_counts_before_narrowing() { + for lhs_bits in [8, 16, 32, 64, 128] { + for rhs_bits in [8, 16, 32, 64, 128] { + for signed in [false, true] { + let count_type = if signed { "i" } else { "u" }; + let module = module(&format!( + "fun probe(x: u{lhs_bits}, n: {count_type}{rhs_bits}) -> u{lhs_bits} {{ return x << n; }}" + )); + let function = &module.functions[0]; + for raw in [0, 1, lhs_bits - 1, lhs_bits, lhs_bits + 1, 256, u128::MAX] { + let bits = raw & mask(rhs_bits); + let negative = signed && bits & (1 << (rhs_bits - 1)) != 0; + assert_eq!( + guard_accepts(function, &[Probe::Bits(1), Probe::Bits(bits)]), + !negative && bits < lhs_bits, + "u{lhs_bits} << {count_type}{rhs_bits}({bits})" + ); + } + } + } + } +} + +#[test] +fn right_shift_opcode_and_result_follow_the_left_operand() { + for (lhs, op) in [("i8", BinOp::AShr), ("u8", BinOp::LShr)] { + let module = module(&format!("fun probe(x: {lhs}, n: u128) -> {lhs} {{ return x >> n; }}")); + let function = &module.functions[0]; + let shift = function.blocks[0] + .instructions + .iter() + .find_map(|i| match i { + Instruction::Bin { op, ty, .. } => Some((op, ty)), + _ => None, + }) + .unwrap(); + assert_eq!(shift.0, &op); + assert_eq!(shift.1, &function.ret_ty); + assert_eq!(width(shift.1), 8); + } +} + +#[test] +fn float_guards_match_exact_truncated_integer_range() { + for source in [32, 64] { + for bits in [8, 16, 32, 64, 128] { + for signed in [false, true] { + let destination = format!("{}{bits}", if signed { "i" } else { "u" }); + let module = module(&format!( + "fun probe(n: f{source}) -> {destination} {{ return n as {destination}; }}" + )); + let upper = 2f64.powi(i32::from(bits - u16::from(signed))); + let mut samples = vec![ + 0.0, + -0.0, + 0.9, + -0.9, + 1.0, + -1.0, + -1.1, + 127.9, + -128.9, + -129.0, + 255.9, + 256.0, + upper, + -upper, + upper - 1.0, + -upper - 1.0, + f64::NAN, + f64::INFINITY, + f64::NEG_INFINITY, + ]; + if source == 32 { + let raw = (upper as f32).to_bits(); + for raw in [raw - 1, raw, raw + 1] { + let value = f32::from_bits(raw) as f64; + samples.extend([value, -value]); + } + } else { + let raw = upper.to_bits(); + for raw in [raw - 1, raw, raw + 1] { + let value = f64::from_bits(raw); + samples.extend([value, -value]); + } + } + for value in samples { + let value = if source == 32 { (value as f32) as f64 } else { value }; + let expected = ConstInt::from_f64(value).is_some_and(|n| n.fits(bits, signed)); + assert_eq!( + guard_accepts(&module.functions[0], &[Probe::Float(value)]), + expected, + "f{source}({value}) -> {destination}" + ); + } + let cast = module.functions[0].blocks[0].instructions.last().unwrap(); + assert!(matches!(cast, Instruction::Cast { op, .. } + if *op == if signed { CastOp::FToI_S } else { CastOp::FToI_U })); + } + } + } +} + +#[test] +fn text_reader_accepts_wave_control_flow_calls_and_ordered_conversions() { + module( + r#" +fun narrow(x: i32) -> i32 { return (x as u8) as i32; } +fun probe(x: i32, n: u64, f: f64) -> i32 { + var value: i32 = x; + while (value > 0) { + value = value - 1; + if (value == 2) { continue; } + if (value == 1) { break; } + } + if (value == 0 || (n < 4 && f > 0.0)) { return narrow(x >> n); } + return f as i32; +} +"#, + ); +} + +#[test] +fn wide_literal_counts_round_trip_without_enabling_wide_runtime_integers() { + let module = module( + r#" +fun literal(x: u128) -> u128 { return x << (127); } +fun maximum() -> u128 { return 340282366920938463463374607431768211455; } +fun skipped(x: i32) -> bool { + return true || (x << 340282366920938463463374607431768211456) == 0; +} +fun nested(x: u8) -> u8 { return (x << 1) >> 1; } +"#, + ); + assert!(guard_accepts(&module.functions[0], &[Probe::Bits(1)])); + let skipped = &module.functions[2]; + assert!(!skipped.blocks[0] + .instructions + .iter() + .any(|i| matches!(i, Instruction::TrapIf { .. }))); + assert!(skipped + .blocks + .iter() + .skip(1) + .any(|b| b.instructions.iter().any(|i| matches!(i, Instruction::TrapIf { .. })))); +} + +#[test] +fn checked_float_cast_keeps_following_integer_conversions_in_order() { + let module = module("fun probe(n: f64) -> i32 { return (n as i8) as i32; }"); + let instructions = &module.functions[0].blocks[0].instructions; + let casts: Vec<_> = instructions + .iter() + .filter_map(|i| match i { + Instruction::Cast { op, src_ty, dst_ty, .. } => Some((op, src_ty, dst_ty)), + _ => None, + }) + .collect(); + assert_eq!( + casts, + vec![(&CastOp::FToI_S, &Type::F64, &Type::I8), (&CastOp::SExt, &Type::I8, &Type::I32),] + ); + assert!(!guard_accepts(&module.functions[0], &[Probe::Float(128.0)])); + assert!(guard_accepts(&module.functions[0], &[Probe::Float(-128.9)])); +} diff --git a/whale/src/codegen/walk.rs b/whale/src/codegen/walk.rs index 944675fc..51cec418 100644 --- a/whale/src/codegen/walk.rs +++ b/whale/src/codegen/walk.rs @@ -9,19 +9,14 @@ enum Action { Convert, Unary, Binary, + WideShift, Assign, Call, LogicalLeft, - LogicalRight { - lhs: ValueId, - lhs_block: BlockId, - end: BlockId, - }, + LogicalRight { lhs: ValueId, lhs_block: BlockId, end: BlockId }, } fn pop(values: &mut Vec) -> Result { - values - .pop() - .ok_or_else(|| ice("expression value stack underflow")) + values.pop().ok_or_else(|| ice("expression value stack underflow")) } impl Lowerer<'_> { @@ -46,6 +41,7 @@ impl Lowerer<'_> { } pop(&mut values) } + fn step<'e>( &mut self, expr: &'e Expression, @@ -64,7 +60,7 @@ impl Lowerer<'_> { match expr.unspanned() { Expression::Literal(literal) => { values.push(self.literal(literal, &fact.evaluation_type)?) - } + }, Expression::Variable(name) => { let slot = self.slot(name)?; let value = self.value(slot.ty.clone()); @@ -75,10 +71,10 @@ impl Lowerer<'_> { align: 1, }); values.push(value); - } + }, Expression::Cast { expr, .. } | Expression::Grouped(expr) => { pending.push((expr, Action::Enter)) - } + }, Expression::Assignment { target, value } | Expression::AssignOperation { target, @@ -90,33 +86,36 @@ impl Lowerer<'_> { } pending.push((expr, Action::Assign)); pending.push((value, Action::Enter)); - } + }, Expression::FunctionCall { args, .. } => { pending.push((expr, Action::Call)); for arg in args.iter().rev() { pending.push((arg, Action::Enter)); } - } + }, Expression::Unary { expr: operand, .. } => { pending.push((expr, Action::Unary)); pending.push((operand, Action::Enter)); - } - Expression::BinaryExpression { - left, - operator, - right, - } => { + }, + Expression::BinaryExpression { left, operator, right } => { if matches!(operator, Operator::LogicalAnd | Operator::LogicalOr) { pending.push((expr, Action::LogicalLeft)); + } else if fact + .shift_count_type + .as_ref() + .and_then(hir::conversions::integer_width) + .is_some_and(|bits| bits > 128) + { + pending.push((expr, Action::WideShift)); } else { pending.push((expr, Action::Binary)); pending.push((right, Action::Enter)); } pending.push((left, Action::Enter)); - } + }, _ => return Err(unsupported("this expression")), } - } + }, Action::Convert => { let mut value = pop(values)?; if value.ty != ty { @@ -132,14 +131,12 @@ impl Lowerer<'_> { })?; } values.push(value); - } + }, Action::Unary => { - let Expression::Unary { operator, .. } = expr.unspanned() else { - unreachable!() - }; + let Expression::Unary { operator, .. } = expr.unspanned() else { unreachable!() }; let operand = pop(values)?; values.push(self.unary_value(operator, operand, fact)?); - } + }, Action::Binary => { let Expression::BinaryExpression { operator, .. } = expr.unspanned() else { unreachable!() @@ -150,21 +147,34 @@ impl Lowerer<'_> { .computation_type .as_ref() .ok_or_else(|| ice("missing computation type"))?; - values.push(self.binary_values(lhs, operator, rhs, computation, &ty)?); - } + values.push(if let Some(count_type) = &fact.shift_count_type { + self.checked_shift(lhs, operator, rhs, computation, count_type)? + } else { + self.binary_values(lhs, operator, rhs, computation, &ty)? + }); + }, + Action::WideShift => { + let Expression::BinaryExpression { operator, right, .. } = expr.unspanned() else { + unreachable!() + }; + let lhs = pop(values)?; + let computation = fact + .computation_type + .as_ref() + .ok_or_else(|| ice("missing shift computation type"))?; + values.push(self.wide_constant_shift(lhs, operator, right, computation)?); + }, Action::Assign => { let (Expression::Assignment { target, .. } | Expression::AssignOperation { target, .. }) = expr.unspanned() else { unreachable!() }; - let Expression::Variable(name) = target.unspanned() else { - unreachable!() - }; + let Expression::Variable(name) = target.unspanned() else { unreachable!() }; let value = pop(values)?; self.store(&self.slot(name)?, value.clone())?; values.push(value); - } + }, Action::Call => { let Expression::FunctionCall { name, args, .. } = expr.unspanned() else { unreachable!() @@ -178,12 +188,9 @@ impl Lowerer<'_> { self.call_values(name, args)? .ok_or_else(|| unsupported("void calls in value expressions"))?, ); - } + }, Action::LogicalLeft => { - let Expression::BinaryExpression { - operator, right, .. - } = expr.unspanned() - else { + let Expression::BinaryExpression { operator, right, .. } = expr.unspanned() else { unreachable!() }; let lhs = pop(values)?; @@ -193,32 +200,14 @@ impl Lowerer<'_> { let lhs_block = BlockId(self.block as u32); let rhs = self.new_block("logical.rhs"); let end = self.new_block("logical.end"); - let (then_bb, else_bb) = if matches!(operator, Operator::LogicalAnd) { - (rhs, end) - } else { - (end, rhs) - }; - self.terminate(Terminator::CBr { - cond: lhs.id, - then_bb, - else_bb, - }); + let (then_bb, else_bb) = + if matches!(operator, Operator::LogicalAnd) { (rhs, end) } else { (end, rhs) }; + self.terminate(Terminator::CBr { cond: lhs.id, then_bb, else_bb }); self.switch(rhs); - pending.push(( - expr, - Action::LogicalRight { - lhs: lhs.id, - lhs_block, - end, - }, - )); + pending.push((expr, Action::LogicalRight { lhs: lhs.id, lhs_block, end })); pending.push((right, Action::Enter)); - } - Action::LogicalRight { - lhs, - lhs_block, - end, - } => { + }, + Action::LogicalRight { lhs, lhs_block, end } => { let rhs = pop(values)?; if rhs.ty != Type::Bool { return Err(ice("logical operand missing HIR bool conversion")); @@ -233,7 +222,7 @@ impl Lowerer<'_> { incomings: vec![(lhs, lhs_block), (rhs.id, rhs_exit)], }); values.push(value); - } + }, } Ok(()) }