Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/cases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ jobs:
"$llvmDirectory\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
"$mingwRoot\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Provision pinned ARM64 libxml2 for LLVM
shell: pwsh
run: ./tools/provision_windows_arm64_libxml2.ps1

- name: Verify native ARM64 toolchains
shell: pwsh
run: |
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,10 @@ jobs:
) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"$llvmDirectory\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Provision pinned ARM64 libxml2 for LLVM
shell: pwsh
run: ./tools/provision_windows_arm64_libxml2.ps1

- name: Verify native ARM64 toolchains
shell: pwsh
run: |
Expand Down Expand Up @@ -1042,7 +1046,7 @@ jobs:
$source = Join-Path $temporary "smoke.wave"
$outputDirectory = Join-Path $temporary "output"
'fun main() { println("release smoke"); }' | Set-Content -Encoding utf8 $source
$output = & $compiler run $source --out-dir $outputDirectory
$output = & $compiler build $source --run --out-dir $outputDirectory
if ($LASTEXITCODE -ne 0 -or $output -ne "release smoke") {
throw "Windows ARM64 packaged compiler smoke test failed: $output"
}
Expand Down
23 changes: 17 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,15 @@ jobs:
python3 -m py_compile x.py tools/check_wave_corpus.py tools/case_manifest.py tools/populate_case_matrix.py tools/run_tests.py tools/test_contracts.py tools/test_case_manifest.py tools/test_test_contracts.py
python3 -m unittest tools.test_case_manifest tools.test_test_contracts

- name: Test Windows ARM64 dependency archive validation
shell: pwsh
run: ./tools/test_windows_arm64_libxml2.ps1

- name: Build release compiler
run: cargo build --locked --release --verbose

- name: Run Rust tests
run: cargo test --locked --all-targets --verbose
run: cargo test --locked --workspace --all-targets --verbose

- name: Check examples and standard library corpus
run: >-
Expand Down Expand Up @@ -328,9 +332,9 @@ jobs:
cargo build --locked --no-default-features
--features llvm-target-aarch64 --jobs 2

- name: Build with the core 64-bit LLVM feature set
- name: Run Rust tests with the core 64-bit LLVM feature set
run: >-
cargo build --locked --no-default-features
cargo test --locked --all-targets --no-default-features
--features llvm-target-core64 --jobs 2

- name: Verify bundled Linux CRT matrix
Expand Down Expand Up @@ -453,10 +457,13 @@ jobs:
- name: Build release compiler
run: cargo build --locked --release --jobs 2

- name: Build LoongArch-only LLVM feature set
- name: Run LoongArch-only LLVM regression tests
env:
WAVE_RUN_LOONGARCH64_INTEROP_TESTS: "1"
run: >-
cargo build --locked --no-default-features
--features llvm-target-loongarch --jobs 2
cargo test --locked --no-default-features
--features llvm-target-loongarch --test codegen_regressions
loongarch64_ --jobs 2

- name: Verify LoongArch64 CRT and target contracts
env:
Expand Down Expand Up @@ -872,6 +879,10 @@ jobs:
) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"$directory\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Provision pinned ARM64 libxml2 for LLVM
shell: pwsh
run: ./tools/provision_windows_arm64_libxml2.ps1

- name: Verify native toolchain
shell: pwsh
run: |
Expand Down
42 changes: 36 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,52 @@ resource-intensive Cargo commands (CI sets `CARGO_BUILD_JOBS=2`).
```bash
cargo fmt --all --check
./tools/check_std_policy.sh
RUSTDOCFLAGS="-D warnings" cargo doc --locked --no-deps --jobs 2
cargo clippy --locked --all-targets -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace --no-deps --jobs 2
cargo clippy --locked --workspace --all-targets -- -D warnings
python3 -m py_compile x.py tools/check_wave_corpus.py tools/case_manifest.py \
tools/populate_case_matrix.py tools/run_tests.py tools/test_contracts.py \
tools/test_case_manifest.py tools/test_test_contracts.py
python3 -m unittest tools.test_case_manifest tools.test_test_contracts
cargo build --locked --release --jobs 2
cargo test --locked --all-targets --verbose
cargo test --locked --workspace --all-targets --verbose
python3 tools/check_wave_corpus.py --wavec target/release/wavec --run-std-examples
```

Notes:

- Formatting must use `cargo fmt --all --check` (not bare `cargo fmt --check`).
- Clippy denies warnings: `cargo clippy --locked --all-targets -- -D warnings`.
- Clippy denies warnings: `cargo clippy --locked --workspace --all-targets -- -D warnings`.
- rustdoc must be warning-free via `RUSTDOCFLAGS="-D warnings"`.
- Standard-library policy is enforced by `./tools/check_std_policy.sh`.
- Wave language corpus / std examples are checked with `tools/check_wave_corpus.py`
after a release `wavec` build.

### Native Windows ARM64 LLVM dependency

The official LLVM 21.1.8 ARM64 MSVC SDK lists `xml2s.lib` in
`llvm-config --system-libs --link-static` without shipping the library.
`llvm-sys` 211 rejects dynamic LLVM linking on MSVC, so `prefer-dynamic`
still falls back to this static dependency.

The build, cases, and release workflows run
`tools/provision_windows_arm64_libxml2.ps1` after installing LLVM. It builds
libxml2 2.13.9 from a SHA-256-pinned
[GNOME source archive](https://download.gnome.org/sources/libxml2/2.13/),
retaining the pre-2.14 XML ABI used by LLVM's static code. The build uses
native ARM64 clang-cl/MSVC tools, the DLL CRT used by default Rust MSVC
builds, and no optional iconv, compression, Python, or XML DLL dependencies.
It supplies the SDK's `xml2s.lib` name and the Windows `bcrypt`/`ws2_32`
imports, then checks every COFF archive member for machine `0xaa64` before
Cargo links it. Release packaging includes the libxml2 copyright notice.
The upstream LLVM release configuration is available in
[the LLVM 21.1.8 release script](https://github.com/llvm/llvm-project/blob/llvmorg-21.1.8/llvm/utils/release/build_llvm_release.bat).

With LLVM tools and PowerShell available, run
`pwsh -NoProfile -File tools/test_windows_arm64_libxml2.ps1` to check the
script syntax and its rejection of x64, mixed, and empty archives. This
portable check complements the actual native Windows ARM64 build and cases;
it does not replace them.

### 4.1 Patch Verification (Maintainers Only)

Maintainers must verify incoming email patches using:
Expand Down Expand Up @@ -152,7 +178,7 @@ All formatting and lint rules must pass:

```bash
cargo fmt --all --check
cargo clippy --locked --all-targets -- -D warnings
cargo clippy --locked --workspace --all-targets -- -D warnings
```

---
Expand All @@ -177,7 +203,7 @@ All additional functionality should be provided through external libraries

Wave uses:

- Locked Rust tests: `cargo test --locked --all-targets`
- Locked Rust tests: `cargo test --locked --workspace --all-targets`
- Automated `.wave` language cases and std examples via
`python3 tools/check_wave_corpus.py`
- Python tooling unit tests: `python3 -m unittest tools.test_case_manifest tools.test_test_contracts`
Expand All @@ -189,6 +215,10 @@ Contributors should:

---

Frontend syntax changes must update [the Alpha grammar and token inventory](spec/README.md)
and its positive/negative fixtures. Run `cargo test --locked -p lexer -p parser --jobs 2`
for backend-independent frontend tests, then the workspace tests for driver and codegen coverage.

## 9. Pull Request Guidelines

A PR should include:
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,6 @@ Wave is developed in public with support from individuals and organizations. You
</p>

Thank you to everyone who contributes code, documentation, testing, funding, or time to Wave.

The [Alpha language contract](spec/README.md) defines the grammar, numeric literals,
token status and source-location conventions checked by frontend conformance tests.
88 changes: 65 additions & 23 deletions front/error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub struct WaveError {
pub source: Option<String>,
pub source_code: Option<String>,
pub span_len: usize,
pub span: Option<crate::SourceSpan>,
pub label: Option<String>,
pub context: Option<String>,
pub expected: Vec<String>,
Expand Down Expand Up @@ -131,6 +132,7 @@ impl WaveError {
source: None,
source_code: None,
span_len: 1,
span: None,
label: None,
context: None,
expected: Vec::new(),
Expand All @@ -152,6 +154,22 @@ impl WaveError {
self
}

pub fn with_span(mut self, span: Option<&crate::SourceSpan>) -> Self {
if let Some(span) = span {
let span = span.focus.as_deref().unwrap_or(span);
self.file = span.file.clone();
self.line = span.line;
self.column = span.column;
self.span_len = if span.line == span.end_line {
span.end_column.saturating_sub(span.column).max(1)
} else {
1
};
self.span = Some(span.clone());
}
self
}

pub fn with_span_len(mut self, span_len: usize) -> Self {
self.span_len = span_len.max(1);
self
Expand Down Expand Up @@ -221,10 +239,26 @@ impl WaveError {
push_json_field(&mut out, "file", &self.file);
out.push_str(&format!(
",\"line\":{},\"column\":{},\"span_len\":{}",
self.line.max(1),
self.column.max(1),
self.line,
self.column,
self.span_len.max(1)
));
out.push_str(",\"span\":");
if let Some(span) = &self.span {
out.push('{');
push_json_field(&mut out, "file", &span.file);
out.push_str(&format!(",\"start\":{},\"end\":{},\"line\":{},\"column\":{},\"end_line\":{},\"end_column\":{}", span.start, span.end, span.line, span.column, span.end_line, span.end_column));
out.push_str(",\"expansion\":[");
for (i, reason) in span.expansion.iter().enumerate() {
if i > 0 {
out.push(',');
}
out.push_str(&json_string(reason));
}
out.push_str("]}");
} else {
out.push_str("null");
}
out.push(',');
push_json_field(
&mut out,
Expand Down Expand Up @@ -333,8 +367,11 @@ impl WaveError {
use utils::colorex::*;

let pipe = "|".color("38,139,235").bold();
let line = self.line.max(1);
let col = self.column.max(1);
if self.line == 0 || self.column == 0 {
return;
}
let line = self.line;
let col = self.column;

if let Some(source_code) = &self.source_code {
let lines: Vec<&str> = source_code.lines().collect();
Expand All @@ -353,19 +390,20 @@ impl WaveError {
pipe,
source_line
);
}

let pad = " ".repeat(width);
let spaces = " ".repeat(col.saturating_sub(1));
let marks = "^"
.repeat(self.span_len.max(1))
.color(self.severity_color())
.bold();
match &self.label {
Some(label) => {
eprintln!(" {} {} {}{} {}", pad, pipe, spaces, marks, label.dim())
if ln == line {
let pad = " ".repeat(width);
let spaces = " ".repeat(col.saturating_sub(1));
let marks = "^"
.repeat(self.span_len.max(1))
.color(self.severity_color())
.bold();
match &self.label {
Some(label) => {
eprintln!(" {} {} {}{} {}", pad, pipe, spaces, marks, label.dim())
}
None => eprintln!(" {} {} {}{}", pad, pipe, spaces, marks),
}
}
None => eprintln!(" {} {} {}{}", pad, pipe, spaces, marks),
}

return;
Expand Down Expand Up @@ -423,13 +461,17 @@ impl WaveError {
eprintln!("{}{}: {}", severity_str, code, self.message.bold());
}

eprintln!(
" {} {}:{}:{}",
"-->".color("38,139,235").bold(),
self.file,
self.line.max(1),
self.column.max(1)
);
if self.line > 0 && self.column > 0 {
eprintln!(
" {} {}:{}:{}",
"-->".color("38,139,235").bold(),
self.file,
self.line,
self.column
);
} else {
eprintln!(" {} {}", "-->".color("38,139,235").bold(), self.file);
}
self.display_source_block();

if let Some(context) = &self.context {
Expand Down
3 changes: 3 additions & 0 deletions front/error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
pub mod error;

pub use error::*;

pub mod span;
pub use span::SourceSpan;
38 changes: 38 additions & 0 deletions front/error/src/span.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! UTF-8 byte ranges with one-based Unicode scalar line/column coordinates.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceSpan {
pub file: String,
pub start: usize,
pub end: usize,
pub line: usize,
pub column: usize,
pub end_line: usize,
pub end_column: usize,
/// Empty for physical syntax; generated syntax records its transformation.
pub expansion: Vec<String>,
/// Optional parser-selected name token for declaration diagnostics.
pub focus: Option<Box<SourceSpan>>,
}

impl SourceSpan {
pub fn through(&self, last: &Self) -> Self {
if self.file != last.file {
return 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<String>) -> Self {
let reason = reason.into();
if let Some(focus) = &mut self.focus {
focus.expansion.push(reason.clone());
}
self.expansion.push(reason);
self
}
}
Loading
Loading