From b81ba15bab23622f2c991f16223df57e4d17e3cf Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 14 Dec 2024 17:53:57 +0100 Subject: [PATCH 01/17] Update Nix flake --- flake.lock | 52 +++++++++------------------------------------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/flake.lock b/flake.lock index a0064b550..75a2f0aa9 100644 --- a/flake.lock +++ b/flake.lock @@ -5,29 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -38,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1710272261, - "narHash": "sha256-g0bDwXFmTE7uGDOs9HcJsfLFhH7fOsASbAuOzDC+fhQ=", + "lastModified": 1733940404, + "narHash": "sha256-Pj39hSoUA86ZePPF/UXiYHHM7hMIkios8TYG29kQT4g=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0ad13a6833440b8e238947e47bea7f11071dc2b2", + "rev": "5d67ea6b4b63378b9c13be21e2ec9d1afc921713", "type": "github" }, "original": { @@ -61,17 +43,16 @@ }, "rust-overlay": { "inputs": { - "flake-utils": "flake-utils_2", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1710382258, - "narHash": "sha256-2FW1q+o34VBweYQiEkRaSEkNMq3ecrn83VzETeGiVbY=", + "lastModified": 1734143514, + "narHash": "sha256-1+r8wYucn8kp9d/IBW1uYGs31QQmSZURElsiOTx65xM=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8ce81e71ab04a7e906fae62da086d6ee5d6cfc21", + "rev": "81fe5c27cb281a9b796d7ad05ad9179e5bd0c78d", "type": "github" }, "original": { @@ -94,21 +75,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", From 1dd897cefc31b50e2317f5a3ef21035b41c49512 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 14 Dec 2024 18:36:49 +0100 Subject: [PATCH 02/17] Update Rust --- compiler_v4/src/main.rs | 2 -- rust-toolchain.toml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler_v4/src/main.rs b/compiler_v4/src/main.rs index 90d90ac37..256285f27 100644 --- a/compiler_v4/src/main.rs +++ b/compiler_v4/src/main.rs @@ -2,9 +2,7 @@ anonymous_lifetime_in_impl_trait, box_patterns, if_let_guard, - iter_repeat_n, let_chains, - option_take_if, try_blocks )] #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9fd746d58..588a32421 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-02-22" +channel = "nightly-2024-11-26" profile = "minimal" components = ["clippy", "rust-src", "rustfmt"] From 06e7b790421759d62572239b6cbe5bd3891818d9 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 14 Dec 2024 21:27:37 +0100 Subject: [PATCH 03/17] Show diagnostics in VS Code Co-Authored-By: Marcel Garus --- Cargo.lock | 39 ++-- compiler_v4/Cargo.toml | 2 + compiler_v4/src/main.rs | 62 +++++++ compiler_v4/src/position.rs | 3 +- vscode_extension_v4/package-lock.json | 13 ++ vscode_extension_v4/package.json | 16 +- .../src/@types/linebyline/index.d.ts | 18 ++ vscode_extension_v4/src/extension.ts | 171 +++++++++++++++++- vscode_extension_v4/tsconfig.json | 4 +- 9 files changed, 304 insertions(+), 24 deletions(-) create mode 100644 vscode_extension_v4/src/@types/linebyline/index.d.ts diff --git a/Cargo.lock b/Cargo.lock index b777ab2e5..2c5fde94b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.48", + "syn 2.0.90", "which", ] @@ -177,6 +177,8 @@ dependencies = [ "petgraph", "replace_with", "rustc-hash 2.0.0", + "serde", + "serde_json", "strum", "tracing", "tracing-subscriber", @@ -573,7 +575,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] @@ -831,7 +833,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] @@ -926,7 +928,7 @@ checksum = "b185e7d068d6820411502efa14d8fbf010750485399402156b72dd2a548ef8e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] @@ -1347,7 +1349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] @@ -1376,9 +1378,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1604,31 +1606,32 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1743,7 +1746,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] @@ -1759,9 +1762,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1935,7 +1938,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.90", ] [[package]] diff --git a/compiler_v4/Cargo.toml b/compiler_v4/Cargo.toml index eac7daea7..446b12f9c 100644 --- a/compiler_v4/Cargo.toml +++ b/compiler_v4/Cargo.toml @@ -18,6 +18,8 @@ itertools = "0.12.0" petgraph = "0.6.5" replace_with = "0.1.7" rustc-hash = "2.0.0" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.133" strum = { version = "0.26.1", features = ["derive"] } tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-subscriber = { version = "0.3.16", features = ["registry"] } diff --git a/compiler_v4/src/main.rs b/compiler_v4/src/main.rs index 256285f27..7bf4d7a77 100644 --- a/compiler_v4/src/main.rs +++ b/compiler_v4/src/main.rs @@ -25,7 +25,10 @@ use clap::{arg, Parser, Subcommand, ValueHint}; use error::CompilerError; use hir::Hir; use hir_to_mono::hir_to_mono; +use itertools::Itertools; use mono_to_c::mono_to_c; +use position::{Position, RangeOfOffset}; +use serde::Serialize; use std::{ fs, path::{Path, PathBuf}, @@ -59,6 +62,7 @@ enum CandyOptions { Debug(DebugOptions), Check(CheckOptions), Compile(CompileOptions), + ToolingAnalyze(ToolingAnalyzeOptions), } fn main() -> ProgramResult { @@ -70,6 +74,7 @@ fn main() -> ProgramResult { CandyOptions::Debug(options) => debug(options), CandyOptions::Check(options) => check(options), CandyOptions::Compile(options) => compile(options), + CandyOptions::ToolingAnalyze(options) => tooling_analyze(options), } } pub type ProgramResult = Result<(), Exit>; @@ -209,6 +214,63 @@ fn compile(options: CompileOptions) -> ProgramResult { Ok(()) } +#[derive(Parser, Debug)] +struct ToolingAnalyzeOptions { + /// The file to analyze. + #[arg(value_hint = ValueHint::FilePath)] + path: PathBuf, +} +#[allow(clippy::needless_pass_by_value)] +fn tooling_analyze(options: ToolingAnalyzeOptions) -> ProgramResult { + let path_str = options.path.to_string_lossy(); + let path = path_str.strip_prefix("file:/").unwrap(); + let source = fs::read_to_string(Path::new(path)) + .unwrap_or_else(|err| panic!("Couldn't open file `{path}`: {err:?}")); + + let (_, errors) = compile_hir(&options.path, &source); + + // TODO: request file content from VS Code extension + + let diagnostics = errors + .into_iter() + .map(|error| { + let span = error.span.to_positions(&source); + Diagnostic { + message: error.message.into_boxed_str(), + source: DiagnosticSource { + file: error.path.to_string_lossy().into_owned().into_boxed_str(), + start: span.start, + end: span.end, + }, + } + }) + .collect_vec(); + println!( + "{}", + serde_json::to_string(&Message::Diagnostics { diagnostics }).unwrap(), + ); + + Ok(()) +} + +#[derive(Serialize)] +#[serde(tag = "type", rename_all = "snake_case")] +enum Message { + ReadFile { path: String }, + Diagnostics { diagnostics: Vec }, +} +#[derive(Serialize)] +struct Diagnostic { + pub message: Box, + pub source: DiagnosticSource, +} +#[derive(Serialize)] +struct DiagnosticSource { + pub file: Box, + pub start: Position, + pub end: Position, +} + fn compile_hir(path: &Path, source: &str) -> (Hir, Vec) { let asts = string_to_ast::string_to_ast(path, source); let mut errors = asts.collect_errors(); diff --git a/compiler_v4/src/position.rs b/compiler_v4/src/position.rs index b6818463a..7077bb906 100644 --- a/compiler_v4/src/position.rs +++ b/compiler_v4/src/position.rs @@ -1,5 +1,6 @@ use derive_more::{Deref, DerefMut, From}; use extension_trait::extension_trait; +use serde::Serialize; use std::{ fmt::{self, Display, Formatter}, ops::Range, @@ -41,7 +42,7 @@ pub impl RangeOfOffset for Range { } } -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Position { /// Zero-based line index (`\n`-separated) pub line: usize, diff --git a/vscode_extension_v4/package-lock.json b/vscode_extension_v4/package-lock.json index fe06ffbef..808c74e3b 100644 --- a/vscode_extension_v4/package-lock.json +++ b/vscode_extension_v4/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "candy", "version": "0.4.0", + "dependencies": { + "linebyline": "^1.3.0" + }, "devDependencies": { "@types/node": "^20.11.20", "@types/vscode": "^1.86.0", @@ -1145,6 +1148,11 @@ "node": ">= 0.8.0" } }, + "node_modules/linebyline": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/linebyline/-/linebyline-1.3.0.tgz", + "integrity": "sha512-3fpIYMrSU77OCf89hjXKuCx6vGwgWEu4N5DDCGqgZ1BF0HYy9V8IbQb/3+VWIU17iBQ83qQoUokH0AhPMOTi7w==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2498,6 +2506,11 @@ "type-check": "~0.4.0" } }, + "linebyline": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/linebyline/-/linebyline-1.3.0.tgz", + "integrity": "sha512-3fpIYMrSU77OCf89hjXKuCx6vGwgWEu4N5DDCGqgZ1BF0HYy9V8IbQb/3+VWIU17iBQ83qQoUokH0AhPMOTi7w==" + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/vscode_extension_v4/package.json b/vscode_extension_v4/package.json index c12afd640..e56780da8 100644 --- a/vscode_extension_v4/package.json +++ b/vscode_extension_v4/package.json @@ -9,7 +9,6 @@ "url": "https://github.com/candy-lang/candy.git", "directory": "vscode_extension_v4" }, - "//": "Keep in sync with `devDependencies.@types/vscode`", "//": "https://github.com/ewanharris/vscode-versions", "engines": { "node": "^18.17.1", @@ -23,6 +22,17 @@ ], "main": "./out/extension.js", "contributes": { + "configuration": { + "title": "🍭 Candy", + "properties": { + "candy.compilerExecutablePath": { + "type": "string", + "default": "", + "markdownDescription": "Path to the 🍭 Candy compiler executable. If empty, we'll attempt to find the 🍭 Candy executable in the `PATH` environment variable.", + "scope": "machine-overridable" + } + } + }, "configurationDefaults": { "[candy]": { "editor.detectIndentation": false, @@ -58,7 +68,9 @@ "lint": "eslint --ext ts .", "watch": "tsc -watch -p ./" }, - "dependencies": {}, + "dependencies": { + "linebyline": "^1.3.0" + }, "devDependencies": { "@types/node": "^20.11.20", "@types/vscode": "^1.86.0", diff --git a/vscode_extension_v4/src/@types/linebyline/index.d.ts b/vscode_extension_v4/src/@types/linebyline/index.d.ts new file mode 100644 index 000000000..88700c055 --- /dev/null +++ b/vscode_extension_v4/src/@types/linebyline/index.d.ts @@ -0,0 +1,18 @@ +declare module "linebyline" { + import { Stream } from "stream"; + + function readLine( + readingObject: string | Stream, + options?: { maxLineLength?: number; retainBuffer?: boolean }, + ): EventEmitter; + export = readLine; + + interface EventEmitter { + on( + event: "line", + listener: (line: string, lineCount: number, byteCount: number) => void, + ): EventEmitter; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + on(event: "error", listener: (error: any) => void): EventEmitter; + } +} diff --git a/vscode_extension_v4/src/extension.ts b/vscode_extension_v4/src/extension.ts index 1067d87fd..8052aed1d 100644 --- a/vscode_extension_v4/src/extension.ts +++ b/vscode_extension_v4/src/extension.ts @@ -1,6 +1,173 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +import * as child_process from "child_process"; +import linebyline from "linebyline"; import * as vscode from "vscode"; +let diagnosticCollection: vscode.DiagnosticCollection; + // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function activate(_context: vscode.ExtensionContext) { - console.log("Activated 🍭 Candy extension!"); +export function activate(context: vscode.ExtensionContext) { + console.info("Activated the 🍭 Candy extension!"); + + diagnosticCollection = vscode.languages.createDiagnosticCollection("candy"); + context.subscriptions.push(diagnosticCollection); + + vscode.window.onDidChangeVisibleTextEditors(() => onlyRunOneAtATime(update)); + vscode.workspace.onDidChangeTextDocument(() => onlyRunOneAtATime(update)); +} + +// Updates can be triggered very frequently (on every keystroke), but they can +// take long – for example, when editing the Candy compiler itself, simply +// analyzing the files takes some time. Thus, here we make sure that only one +// update runs at a time. +let generation = 0; +let currentRun = Promise.resolve(null); +async function onlyRunOneAtATime(callback: () => Promise) { + console.log("Scheduling update"); + const myGeneration = ++generation; + await currentRun; + if (generation != myGeneration) return; // a newer update exists and will run + // eslint-disable-next-line @typescript-eslint/no-misused-promises, no-async-promise-executor + currentRun = new Promise(async (resolve) => { + await callback(); + resolve(null); + }); +} + +async function update() { + console.log("Updating"); + const promises = []; + for (const editor of vscode.window.visibleTextEditors) { + const uri = editor.document.uri.toString(); + if (!uri.endsWith(".candy")) continue; + + const analysis = analyze(uri); + promises.push(analysis); + analysis + .then((diagnostics) => { + diagnosticCollection.clear(); + const diagnosticMap = new Map(); + for (const diagnostic of diagnostics) { + console.info(`Error: ${JSON.stringify(diagnostic)}`); + if (diagnostic.source.file != uri) continue; + const range = new vscode.Range( + new vscode.Position( + diagnostic.source.start.line, + diagnostic.source.start.character, + ), + new vscode.Position( + diagnostic.source.end.line, + diagnostic.source.end.character, + ), + ); + const diagnostics = diagnosticMap.get(diagnostic.source.file) ?? []; + diagnostics.push( + new vscode.Diagnostic( + range, + diagnostic.message, + vscode.DiagnosticSeverity.Error, + ), + ); + diagnosticMap.set(diagnostic.source.file, diagnostics); + } + diagnosticMap.forEach((diags, file) => { + diagnosticCollection.set(vscode.Uri.parse(file), diags); + }); + }) + .catch((error) => { + console.error(`Analyzing failed: ${error}`); + }); + } + await Promise.all(promises); +} + +/// Communication with the Candy language server works using the following +/// schema. + +type AnalyzeMessage = ReadFileMessage | DiagnosticsMessage; // candy tooling-analyze ... +interface ReadFileMessage { + type: "read_file"; + path: string; +} +interface DiagnosticsMessage { + type: "diagnostics"; + diagnostics: Diagnostic[]; +} +interface Diagnostic { + message: string; + source: { + file: string; + start: { line: number; character: number }; + end: { line: number; character: number }; + }; +} + +async function analyze(path: string): Promise { + console.log(`Analyzing ${path}`); + const candy = child_process.spawn( + vscode.workspace + .getConfiguration("candy") + .get("compilerExecutablePath") ?? "candy", + ["tooling-analyze", path], + { env: { ...process.env, RUST_BACKTRACE: "1" } }, + ); + candy.on("error", (error) => { + console.error(`Failed to spawn: ${error.name}: ${error.message}`); + }); + linebyline(candy.stderr).on("line", (line: string) => { + console.log(line); + }); + + let diagnostics: Diagnostic[] = []; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + linebyline(candy.stdout).on("line", async function (line: string) { + console.info("Line: " + line); + const message = JSON.parse(line) as AnalyzeMessage; + switch (message.type) { + case "read_file": + candy.stdin.write(await handleReadFileMessage(message)); + break; + case "diagnostics": + diagnostics = message.diagnostics; + break; + } + }); + + const exitCode: number | null = await new Promise((resolve) => + candy.on("close", (exitCode) => { + resolve(exitCode); + }), + ); + console.info(`\`candy tooling-analyze\` exited with exit code ${exitCode}.`); + + return diagnostics; +} + +async function handleReadFileMessage( + message: ReadFileMessage, +): Promise { + const uri = vscode.Uri.parse(message.path); + const content = await readFile(uri); + const response = content + ? { type: "read_file", success: true, content: content } + : { type: "read_file", success: false }; + return `${JSON.stringify(response)}\n`; +} + +/** + * Returns the source code of the given URI. Prefers the content of open text + * documents, even if they're not saved yet. If none exists, asks the file + * system. + */ +async function readFile(uri: vscode.Uri): Promise { + for (const doc of vscode.workspace.textDocuments) { + if (doc.uri.toString() == uri.toString()) return doc.getText(); + } + + try { + const bytes = await vscode.workspace.fs.readFile(uri); + return new TextDecoder("utf8").decode(bytes); + } catch (e) { + return null; + } } diff --git a/vscode_extension_v4/tsconfig.json b/vscode_extension_v4/tsconfig.json index 865bc376b..50bc10384 100644 --- a/vscode_extension_v4/tsconfig.json +++ b/vscode_extension_v4/tsconfig.json @@ -10,6 +10,8 @@ "noImplicitAny": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noUnusedParameters": true + "noUnusedParameters": true, + "esModuleInterop": true, + "typeRoots": ["src/@types", "node_modules/@types"] } } From fd353460c4d3d922654741604e31778b5badd4da Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 14 Dec 2024 21:45:31 +0100 Subject: [PATCH 04/17] Fix linter complaints --- Cargo.toml | 2 +- compiler_v4/src/ast.rs | 1 + compiler_v4/src/ast_to_hir.rs | 36 +++++++++---------- compiler_v4/src/hir.rs | 2 +- compiler_v4/src/hir_to_mono.rs | 6 ++-- compiler_v4/src/main.rs | 18 ++++++---- compiler_v4/src/mono.rs | 4 +-- compiler_v4/src/mono_to_c.rs | 18 +++++----- compiler_v4/src/string_to_ast/declarations.rs | 2 +- compiler_v4/src/string_to_ast/expression.rs | 4 +-- compiler_v4/src/string_to_ast/parser.rs | 11 +++--- compiler_v4/src/string_to_ast/type_.rs | 2 +- compiler_v4/src/type_solver/goals.rs | 2 +- 13 files changed, 56 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b61421a4..fff06d9d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ members = [ [workspace.package] edition = "2021" -rust-version = "1.78.0" +rust-version = "1.80.0" [profile.release] # This adds file and line number information to backtraces while only increasing diff --git a/compiler_v4/src/ast.rs b/compiler_v4/src/ast.rs index 72e805db8..4bdd468e4 100644 --- a/compiler_v4/src/ast.rs +++ b/compiler_v4/src/ast.rs @@ -175,6 +175,7 @@ pub struct AstTypeParameter { // Types +#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum AstType { Named(AstNamedType), diff --git a/compiler_v4/src/ast_to_hir.rs b/compiler_v4/src/ast_to_hir.rs index 12a897b5e..a69541b39 100644 --- a/compiler_v4/src/ast_to_hir.rs +++ b/compiler_v4/src/ast_to_hir.rs @@ -130,7 +130,7 @@ struct ImplDeclaration<'a> { trait_: Trait, functions: FxHashMap>, } -impl<'a> ImplDeclaration<'a> { +impl ImplDeclaration<'_> { #[must_use] fn into_impl(self) -> Impl { Impl { @@ -166,7 +166,7 @@ struct FunctionDeclaration<'a> { signature: Signature, body: Option, } -impl<'a> FunctionDeclaration<'a> { +impl FunctionDeclaration<'_> { fn signature_to_string(&self) -> String { format!("{}{}", self.name, self.signature) } @@ -1195,7 +1195,7 @@ impl<'a> Context<'a> { .collect_vec(), self_base_type, |builder| { - for parameter in function.signature.parameters.iter() { + for parameter in &function.signature.parameters { builder.push_parameter(parameter.clone()); } @@ -1229,7 +1229,7 @@ impl<'a> Context<'a> { .unwrap() } - fn get_all_functions_matching_name(&mut self, name: &str) -> Vec { + fn get_all_functions_matching_name(&self, name: &str) -> Vec { self.functions .iter() .chain(self.traits.iter().flat_map(|(_, trait_)| &trait_.functions)) @@ -1285,7 +1285,9 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { self.type_parameters, self.self_base_type, |builder| { - builder.local_identifiers = self.local_identifiers.clone(); + builder + .local_identifiers + .clone_from(&self.local_identifiers); fun(builder); self.global_assignment_dependencies .extend(&builder.global_assignment_dependencies); @@ -1433,14 +1435,12 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { type_: NamedType::text().into(), } } - AstExpressionKind::Parenthesized(parenthesized) => { - return parenthesized - .inner - .value() - .map_or(LoweredExpression::Error, |it| { - self.lower_expression_raw(it, context_type) - }); - } + AstExpressionKind::Parenthesized(parenthesized) => parenthesized + .inner + .value() + .map_or(LoweredExpression::Error, |it| { + self.lower_expression_raw(it, context_type) + }), AstExpressionKind::Call(call) => { let type_arguments = call.type_arguments.as_ref().map(|it| { it.arguments @@ -1527,7 +1527,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { type_arguments.as_deref(), &type_, &type_declaration.type_parameters, - fields, + fields.as_deref(), ) } TypeDeclarationKind::Enum { .. } => { @@ -1670,7 +1670,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { ); let body = self.build_inner(|builder| { - for parameter in parameters.iter() { + for parameter in ¶meters { builder.push_parameter(parameter.clone()); } @@ -1861,7 +1861,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { } else if let Some(type_parameter) = self.type_parameters.iter().find(|it| it.name == *name) { LoweredExpression::TypeParameterReference(type_parameter.type_()) - } else if self.context.hir.type_declarations.get(name).is_some() { + } else if self.context.hir.type_declarations.contains_key(name) { LoweredExpression::NamedTypeReference(name.clone()) } else { self.context.add_error( @@ -2059,7 +2059,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { type_arguments: Option<&[Type]>, type_: &str, type_parameters: &[TypeParameter], - fields: &Option>, + fields: Option<&[StructField]>, ) -> LoweredExpression { let Some(fields) = fields else { self.context.add_error( @@ -2213,7 +2213,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { .collect::>() } fn match_signature( - &mut self, + &self, trait_goal_and_subgoals: Option<(&SolverGoal, &[SolverGoal])>, type_parameters: &[TypeParameter], parameter_types: &[Type], diff --git a/compiler_v4/src/hir.rs b/compiler_v4/src/hir.rs index 17cb40901..74bd2605a 100644 --- a/compiler_v4/src/hir.rs +++ b/compiler_v4/src/hir.rs @@ -117,7 +117,7 @@ impl ToText for Hir { (name.as_ref(), definition).build_text(builder); builder.push_newline(); } - for impl_ in self.impls.iter() { + for impl_ in &self.impls { impl_.build_text(builder); builder.push_newline(); } diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index c1a4469a5..38af06c44 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -358,7 +358,7 @@ impl<'h> Context<'h> { result.push_str(&type_.name); if !type_.type_arguments.is_empty() { result.push_str("$of$"); - for type_ in type_.type_arguments.iter() { + for type_ in &type_.type_arguments { Self::mangle_type_helper(result, type_); result.push('$'); } @@ -375,7 +375,7 @@ impl<'h> Context<'h> { result.push_str("$Fun$"); if !type_.parameter_types.is_empty() { result.push_str("of$"); - for type_ in type_.parameter_types.iter() { + for type_ in &type_.parameter_types { Self::mangle_type_helper(result, type_); result.push('$'); } @@ -427,7 +427,7 @@ impl<'c, 'h> BodyBuilder<'c, 'h> { id_generator: IdGenerator::default(), id_mapping: FxHashMap::default(), }; - builder.id_mapping = self.id_mapping.clone(); + builder.id_mapping.clone_from(&self.id_mapping); builder.id_generator = mem::take(&mut self.id_generator); fun(&mut builder); diff --git a/compiler_v4/src/main.rs b/compiler_v4/src/main.rs index 7bf4d7a77..875d8ca2e 100644 --- a/compiler_v4/src/main.rs +++ b/compiler_v4/src/main.rs @@ -74,7 +74,10 @@ fn main() -> ProgramResult { CandyOptions::Debug(options) => debug(options), CandyOptions::Check(options) => check(options), CandyOptions::Compile(options) => compile(options), - CandyOptions::ToolingAnalyze(options) => tooling_analyze(options), + CandyOptions::ToolingAnalyze(options) => { + tooling_analyze(options); + Ok(()) + } } } pub type ProgramResult = Result<(), Exit>; @@ -221,7 +224,7 @@ struct ToolingAnalyzeOptions { path: PathBuf, } #[allow(clippy::needless_pass_by_value)] -fn tooling_analyze(options: ToolingAnalyzeOptions) -> ProgramResult { +fn tooling_analyze(options: ToolingAnalyzeOptions) { let path_str = options.path.to_string_lossy(); let path = path_str.strip_prefix("file:/").unwrap(); let source = fs::read_to_string(Path::new(path)) @@ -249,15 +252,18 @@ fn tooling_analyze(options: ToolingAnalyzeOptions) -> ProgramResult { "{}", serde_json::to_string(&Message::Diagnostics { diagnostics }).unwrap(), ); - - Ok(()) } #[derive(Serialize)] #[serde(tag = "type", rename_all = "snake_case")] enum Message { - ReadFile { path: String }, - Diagnostics { diagnostics: Vec }, + #[allow(dead_code)] + ReadFile { + path: String, + }, + Diagnostics { + diagnostics: Vec, + }, } #[derive(Serialize)] struct Diagnostic { diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index 10af013d7..53d5c1afd 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -96,7 +96,7 @@ impl ToText for Mono { builder.push("Assignments (in initialization order):"); builder.push_newline(); - for name in self.assignment_initialization_order.iter() { + for name in &self.assignment_initialization_order { (&**name, &self.assignments[name]).build_text(builder); builder.push_newline(); } @@ -264,7 +264,7 @@ impl Body { } ExpressionKind::Switch { value, cases, .. } => { referenced_ids.insert(*value); - for case in cases.iter() { + for case in &**cases { if let Some((value_id, _)) = &case.value { referenced_ids.insert(*value_id); } diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index aa133f668..9f01d9e89 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -58,7 +58,7 @@ impl<'h> Context<'h> { self.lower_function_definitions(); self.push("int main() {\n"); - for name in self.mono.assignment_initialization_order.iter() { + for name in &self.mono.assignment_initialization_order { self.push(format!("{name}$init();\n")); } self.push(format!( @@ -99,7 +99,7 @@ impl<'h> Context<'h> { self.push("};\n"); } TypeDeclaration::Struct { fields } => { - for (name, type_) in fields.iter() { + for (name, type_) in &**fields { self.push(format!("{type_}* {name}; ")); } self.push("};\n"); @@ -107,14 +107,14 @@ impl<'h> Context<'h> { TypeDeclaration::Enum { variants } => { if !variants.is_empty() { self.push("enum {"); - for variant in variants.iter() { + for variant in &**variants { self.push(format!("{name}_{},", variant.name)); } self.push("} variant;\n"); } self.push("union {"); - for variant in variants.iter() { + for variant in &**variants { if let Some(value_type) = &variant.value_type { self.push(format!("{value_type}* {};", variant.name)); } @@ -127,7 +127,7 @@ impl<'h> Context<'h> { } => { self.push("void* closure;\n"); self.push(format!("{return_type}* (*function)(void*")); - for parameter_type in parameter_types.iter() { + for parameter_type in &**parameter_types { self.push(format!(", {parameter_type}*")); } self.push(");\n"); @@ -230,7 +230,7 @@ impl<'h> Context<'h> { "{}* {declaration_name}$lambda{id}_function(void* raw_closure", &lambda.body.return_type() )); - for parameter in lambda.parameters.iter() { + for parameter in &lambda.parameters { self.push(format!(", {}* {}", ¶meter.type_, parameter.id)); } self.push(")"); @@ -249,7 +249,7 @@ impl<'h> Context<'h> { | ExpressionKind::CallFunction { .. } | ExpressionKind::CallLambda { .. } => {} ExpressionKind::Switch { cases, .. } => { - for case in cases.iter() { + for case in &**cases { Self::visit_lambdas_inside_body(&case.body, visitor); } } @@ -825,7 +825,7 @@ impl<'h> Context<'h> { "{}* {id} = {lambda}->function({lambda}->closure", &expression.type_ )); - for argument in arguments.iter() { + for argument in &**arguments { self.push(format!(", {argument}")); } self.push(");"); @@ -838,7 +838,7 @@ impl<'h> Context<'h> { self.push(format!("{}* {id};\n", &expression.type_)); self.push(format!("switch ({value}->variant) {{")); - for case in cases.iter() { + for case in &**cases { self.push(format!("case {enum_}_{}:\n", case.variant)); if let Some((value_id, value_type)) = &case.value { self.push(format!( diff --git a/compiler_v4/src/string_to_ast/declarations.rs b/compiler_v4/src/string_to_ast/declarations.rs index e13be0960..86b93b0a6 100644 --- a/compiler_v4/src/string_to_ast/declarations.rs +++ b/compiler_v4/src/string_to_ast/declarations.rs @@ -418,7 +418,7 @@ fn parameter<'a>(parser: Parser) -> Option<(Parser, AstParameter, Option } #[instrument(level = "trace")] -fn type_parameters<'s>(parser: Parser<'s>) -> Option<(Parser, AstTypeParameters)> { +fn type_parameters(parser: Parser) -> Option<(Parser, AstTypeParameters)> { let start_offset = parser.offset(); let mut parser = opening_bracket(parser)?.and_trailing_whitespace(); diff --git a/compiler_v4/src/string_to_ast/expression.rs b/compiler_v4/src/string_to_ast/expression.rs index 7db0ba8c0..9967284da 100644 --- a/compiler_v4/src/string_to_ast/expression.rs +++ b/compiler_v4/src/string_to_ast/expression.rs @@ -53,7 +53,7 @@ pub fn expression(parser: Parser) -> Option<(Parser, AstExpression)> { result: &mut AstExpression, parse: fn(Parser<'a>, &mut AstExpression) -> Option>, ) -> bool { - parse(*parser, result).map_or(false, |new_parser| { + parse(*parser, result).is_some_and(|new_parser| { *parser = new_parser; true }) @@ -290,7 +290,7 @@ fn expression_suffix_call<'s>( Some(parser) } #[instrument(level = "trace")] -fn arguments<'s>(parser: Parser<'s>) -> Option<(Parser, AstArguments)> { +fn arguments<'s>(parser: Parser<'s>) -> Option<(Parser<'s>, AstArguments)> { let opening_parenthesis_start = parser.offset(); let mut parser = opening_parenthesis(parser)?.and_trailing_whitespace(); let opening_parenthesis_span = opening_parenthesis_start..parser.offset(); diff --git a/compiler_v4/src/string_to_ast/parser.rs b/compiler_v4/src/string_to_ast/parser.rs index d0bd5df1c..cee2e5bee 100644 --- a/compiler_v4/src/string_to_ast/parser.rs +++ b/compiler_v4/src/string_to_ast/parser.rs @@ -75,7 +75,7 @@ impl<'s> Parser<'s> { } #[must_use] - pub fn consume_literal(mut self, literal: &'static str) -> Option> { + pub fn consume_literal(mut self, literal: &'static str) -> Option { if self.rest().starts_with(literal) { self.offset = Offset(*self.offset + literal.len()); Some(self) @@ -87,7 +87,7 @@ impl<'s> Parser<'s> { pub fn consume_while_not_empty( self, predicate: impl FnMut(char) -> bool, - ) -> Option<(Parser<'s>, AstString)> { + ) -> Option<(Self, AstString)> { let (parser, string) = self.consume_while(predicate); if string.is_empty() { None @@ -96,10 +96,7 @@ impl<'s> Parser<'s> { } } #[must_use] - pub fn consume_while( - mut self, - mut predicate: impl FnMut(char) -> bool, - ) -> (Parser<'s>, AstString) { + pub fn consume_while(mut self, mut predicate: impl FnMut(char) -> bool) -> (Self, AstString) { let start_offset = self.offset(); while let Some((new_parser, c)) = self.consume_char() && predicate(c) @@ -109,7 +106,7 @@ impl<'s> Parser<'s> { (self, self.string(start_offset)) } #[must_use] - pub fn consume_char(self) -> Option<(Parser<'s>, char)> { + pub fn consume_char(self) -> Option<(Self, char)> { self.next_char().map(|c| { ( Parser { diff --git a/compiler_v4/src/string_to_ast/type_.rs b/compiler_v4/src/string_to_ast/type_.rs index 4fc416a42..a200f4a6e 100644 --- a/compiler_v4/src/string_to_ast/type_.rs +++ b/compiler_v4/src/string_to_ast/type_.rs @@ -93,7 +93,7 @@ fn function_type_parameter_type<'a>( } #[instrument(level = "trace")] -pub fn type_arguments<'s>(parser: Parser<'s>) -> Option<(Parser, AstTypeArguments)> { +pub fn type_arguments<'s>(parser: Parser<'s>) -> Option<(Parser<'s>, AstTypeArguments)> { let start_offset = parser.offset(); let mut parser = opening_bracket(parser)?.and_trailing_whitespace(); diff --git a/compiler_v4/src/type_solver/goals.rs b/compiler_v4/src/type_solver/goals.rs index 52ef2291c..ea84ab593 100644 --- a/compiler_v4/src/type_solver/goals.rs +++ b/compiler_v4/src/type_solver/goals.rs @@ -280,7 +280,7 @@ struct SolverTree { goal: SolverGoal, } impl SolverTree { - fn solve(&mut self, context: &mut Solver) -> SolverSolution { + fn solve(&self, context: &mut Solver) -> SolverSolution { let mut strands = context .rules .iter() From dd7b7ccd4765a137909ff53ebf9b53254a2d67e1 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 19 Dec 2024 09:30:30 +0100 Subject: [PATCH 05/17] Disable Clippy, fix lints in old crates --- compiler/backend_inkwell/src/lib.rs | 21 +++++++++++---------- compiler/cli/src/main.rs | 22 +++++++++++----------- compiler/formatter/src/lib.rs | 15 ++++++++------- compiler/frontend/src/lib.rs | 22 +++++++++++----------- compiler/fuzzer/src/lib.rs | 5 +++-- compiler/language_server/src/lib.rs | 24 ++++++++++++------------ compiler/vm/benches/benchmark.rs | 2 +- compiler/vm/src/instruction_pointer.rs | 8 ++++---- compiler/vm/src/lib.rs | 22 +++++++++++----------- third_party/dap-rs/src/events.rs | 8 ++++---- third_party/dap-rs/src/types.rs | 2 +- 11 files changed, 77 insertions(+), 74 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 1c633e86a..80b8848e3 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -1,14 +1,15 @@ #![feature(let_chains)] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow( - clippy::cognitive_complexity, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::similar_names, - clippy::too_many_lines -)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow( +// clippy::cognitive_complexity, +// clippy::match_same_arms, +// clippy::missing_errors_doc, +// clippy::missing_panics_doc, +// clippy::module_name_repetitions, +// clippy::similar_names, +// clippy::too_many_lines +// )] use candy_frontend::{ builtin_functions::BuiltinFunction, diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index 20e34d609..8d33f747f 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -1,14 +1,14 @@ -#![feature(lazy_cell)] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow( - clippy::cognitive_complexity, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::similar_names, - clippy::too_many_lines -)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow( +// clippy::cognitive_complexity, +// clippy::match_same_arms, +// clippy::missing_errors_doc, +// clippy::missing_panics_doc, +// clippy::module_name_repetitions, +// clippy::similar_names, +// clippy::too_many_lines +// )] use candy_vm::CAN_USE_STDOUT; use clap::Parser; diff --git a/compiler/formatter/src/lib.rs b/compiler/formatter/src/lib.rs index 6b3ef252d..fb69889f9 100644 --- a/compiler/formatter/src/lib.rs +++ b/compiler/formatter/src/lib.rs @@ -4,13 +4,14 @@ const_trait_impl, let_chains )] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow( - clippy::cognitive_complexity, - clippy::match_same_arms, - clippy::module_name_repetitions, - clippy::too_many_lines -)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow( +// clippy::cognitive_complexity, +// clippy::match_same_arms, +// clippy::module_name_repetitions, +// clippy::too_many_lines +// )] use candy_frontend::{cst::Cst, position::Offset}; use existing_whitespace::{TrailingWithIndentationConfig, WhitespacePositionInBody}; diff --git a/compiler/frontend/src/lib.rs b/compiler/frontend/src/lib.rs index 9152212f6..bd9ec48c5 100644 --- a/compiler/frontend/src/lib.rs +++ b/compiler/frontend/src/lib.rs @@ -1,23 +1,23 @@ #![feature( anonymous_lifetime_in_impl_trait, box_patterns, - entry_insert, extract_if, hasher_prefixfree_extras, io_error_more, let_chains, try_blocks )] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow( - clippy::cognitive_complexity, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::similar_names, - clippy::too_many_lines -)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow( +// clippy::cognitive_complexity, +// clippy::match_same_arms, +// clippy::missing_errors_doc, +// clippy::missing_panics_doc, +// clippy::module_name_repetitions, +// clippy::similar_names, +// clippy::too_many_lines +// )] pub use self::tracing::{CallTracingMode, TracingConfig, TracingMode}; diff --git a/compiler/fuzzer/src/lib.rs b/compiler/fuzzer/src/lib.rs index c0fbeb607..a3603e280 100644 --- a/compiler/fuzzer/src/lib.rs +++ b/compiler/fuzzer/src/lib.rs @@ -1,6 +1,7 @@ #![feature(let_chains, round_char_boundary)] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)] mod coverage; mod fuzzer; diff --git a/compiler/language_server/src/lib.rs b/compiler/language_server/src/lib.rs index 36fe2c5c6..9795cc0a7 100644 --- a/compiler/language_server/src/lib.rs +++ b/compiler/language_server/src/lib.rs @@ -2,19 +2,19 @@ anonymous_lifetime_in_impl_trait, async_closure, box_patterns, - let_chains, - strict_provenance -)] -#![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] -#![allow( - clippy::future_not_send, // TODO: Fix clippy::future_not_send occurrences - clippy::large_enum_variant, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::too_many_lines + let_chains )] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] +// #![allow( +// clippy::future_not_send, // TODO: Fix clippy::future_not_send occurrences +// clippy::large_enum_variant, +// clippy::match_same_arms, +// clippy::missing_errors_doc, +// clippy::missing_panics_doc, +// clippy::module_name_repetitions, +// clippy::too_many_lines +// )] pub mod database; pub mod debug_adapter; diff --git a/compiler/vm/benches/benchmark.rs b/compiler/vm/benches/benchmark.rs index 5ddbfb09a..092baad30 100644 --- a/compiler/vm/benches/benchmark.rs +++ b/compiler/vm/benches/benchmark.rs @@ -1,5 +1,5 @@ -#![feature(absolute_path)] #![allow(unused_attributes)] +#![allow(clippy::all)] use candy_frontend::module::PackagesPath; use candy_vm::{ diff --git a/compiler/vm/src/instruction_pointer.rs b/compiler/vm/src/instruction_pointer.rs index 7a393205a..be6bc432c 100644 --- a/compiler/vm/src/instruction_pointer.rs +++ b/compiler/vm/src/instruction_pointer.rs @@ -17,16 +17,16 @@ impl InstructionPointer { } } impl Step for InstructionPointer { - fn steps_between(start: &Self, end: &Self) -> Option { - (**end).checked_sub(**start) + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + usize::steps_between(&**start, &**end) } fn forward_checked(start: Self, count: usize) -> Option { - (*start).checked_add(count).map(Self) + usize::forward_checked(*start, count).map(InstructionPointer) } fn backward_checked(start: Self, count: usize) -> Option { - (*start).checked_sub(count).map(Self) + usize::backward_checked(*start, count).map(InstructionPointer) } } impl Debug for InstructionPointer { diff --git a/compiler/vm/src/lib.rs b/compiler/vm/src/lib.rs index bec1adb15..3eae98833 100644 --- a/compiler/vm/src/lib.rs +++ b/compiler/vm/src/lib.rs @@ -8,7 +8,6 @@ nonzero_ops, slice_ptr_get, step_trait, - strict_provenance, try_blocks )] // We can't enable `unused_crate_dependencies` since it reports false positives about @@ -16,16 +15,17 @@ // https://github.com/rust-lang/rust/issues/57274 // https://github.com/rust-lang/rust/issues/95513 // https://github.com/rust-lang/rust-clippy/issues/4341 -#![warn(clippy::nursery, clippy::pedantic)] -#![allow( - clippy::large_enum_variant, - clippy::match_same_arms, - clippy::missing_errors_doc, - clippy::missing_panics_doc, - clippy::module_name_repetitions, - clippy::similar_names, - clippy::too_many_lines -)] +#![allow(clippy::all)] +// #![warn(clippy::nursery, clippy::pedantic)] +// #![allow( +// clippy::large_enum_variant, +// clippy::match_same_arms, +// clippy::missing_errors_doc, +// clippy::missing_panics_doc, +// clippy::module_name_repetitions, +// clippy::similar_names, +// clippy::too_many_lines +// )] pub use builtin_functions::CAN_USE_STDOUT; pub use instruction_pointer::InstructionPointer; diff --git a/third_party/dap-rs/src/events.rs b/third_party/dap-rs/src/events.rs index 2ebf88ed6..e507391ee 100644 --- a/third_party/dap-rs/src/events.rs +++ b/third_party/dap-rs/src/events.rs @@ -260,18 +260,18 @@ pub struct StoppedEventBody { /// If `allThreadsStopped` is true, a debug adapter can announce that all /// threads have stopped. /// - The client should use this information to enable that all threads can - /// be expanded to access their stacktraces. + /// be expanded to access their stacktraces. /// - If the attribute is missing or false, only the thread with the given - /// `threadId` can be expanded. + /// `threadId` can be expanded. pub all_threads_stopped: Option, /// Ids of the breakpoints that triggered the event. In most cases there is /// only a single breakpoint but here are some examples for multiple /// breakpoints: /// - Different types of breakpoints map to the same location. /// - Multiple source breakpoints get collapsed to the same instruction by - /// the compiler/runtime. + /// the compiler/runtime. /// - Multiple function breakpoints with different function names map to the - /// same location. + /// same location. pub hit_breakpoint_ids: Option>, } diff --git a/third_party/dap-rs/src/types.rs b/third_party/dap-rs/src/types.rs index 656b1d2b5..bb88b9ef4 100644 --- a/third_party/dap-rs/src/types.rs +++ b/third_party/dap-rs/src/types.rs @@ -1156,7 +1156,7 @@ fromstr_deser! { VariablesArgumentsFilter } tostr_ser! { VariablesArgumentsFilter } /// Properties of a breakpoint location returned from the breakpointLocations request. - +/// /// Specfication: [BreakpointLocation](https://microsoft.github.io/debug-adapter-protocol/specification#Types_BreakpointLocation) #[derive(Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] From 849285b9cf93432790a41e31fee858ceb015aca0 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 19 Dec 2024 13:55:32 +0100 Subject: [PATCH 06/17] Add enums for builtin types --- compiler_v4/src/ast_to_hir.rs | 64 ++++++++++++++++++++++++++-------- compiler_v4/src/hir.rs | 42 +++++++++++++++------- compiler_v4/src/hir_to_mono.rs | 44 ++++++++++++----------- compiler_v4/src/mono.rs | 34 +++++++++--------- compiler_v4/src/mono_to_c.rs | 23 +++++------- 5 files changed, 129 insertions(+), 78 deletions(-) diff --git a/compiler_v4/src/ast_to_hir.rs b/compiler_v4/src/ast_to_hir.rs index a69541b39..f413cf5a9 100644 --- a/compiler_v4/src/ast_to_hir.rs +++ b/compiler_v4/src/ast_to_hir.rs @@ -7,9 +7,9 @@ use crate::{ }, error::CompilerError, hir::{ - self, Assignment, Body, BodyOrBuiltin, BuiltinFunction, ContainsError, Expression, - ExpressionKind, Function, FunctionSignature, FunctionType, Hir, Id, Impl, NamedType, - Parameter, ParameterType, SliceOfTypeParameter, StructField, SwitchCase, Trait, + self, Assignment, Body, BodyOrBuiltin, BuiltinFunction, BuiltinType, ContainsError, + Expression, ExpressionKind, Function, FunctionSignature, FunctionType, Hir, Id, Impl, + NamedType, Parameter, ParameterType, SliceOfTypeParameter, StructField, SwitchCase, Trait, TraitDefinition, TraitFunction, Type, TypeDeclaration, TypeDeclarationKind, TypeParameter, }, id::IdGenerator, @@ -501,10 +501,23 @@ impl<'a> Context<'a> { self.lower_type_parameters(&[], None, struct_type.type_parameters.as_ref()); let self_base_type = NamedType::new(name.string.clone(), type_parameters.type_()).into(); - let fields = match &struct_type.kind { - AstStructKind::Builtin { .. } => None, - AstStructKind::UserDefined { fields, .. } => Some( - fields + let kind = match &struct_type.kind { + AstStructKind::Builtin { .. } => TypeDeclarationKind::Builtin(match &*name.string { + "Int" => BuiltinType::Int, + "List" => BuiltinType::List(Box::new( + type_parameters.iter().next().unwrap().type_().into(), + )), + "Text" => BuiltinType::Text, + _ => { + self.add_error( + name.span.clone(), + format!("Unknown builtin type: `{}`", name.string), + ); + return; + } + }), + AstStructKind::UserDefined { fields, .. } => TypeDeclarationKind::Struct { + fields: fields .iter() .filter_map(|field| { let name = field.name.value()?; @@ -520,7 +533,7 @@ impl<'a> Context<'a> { }) }) .collect(), - ), + }, }; if self.traits.contains_key(&name.string) { @@ -540,7 +553,7 @@ impl<'a> Context<'a> { Entry::Vacant(entry) => { entry.insert(TypeDeclaration { type_parameters, - kind: TypeDeclarationKind::Struct { fields }, + kind, }); } }; @@ -1473,7 +1486,8 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { let type_declaration = self.context.hir.type_declarations[&type_].clone(); match &type_declaration.kind { - TypeDeclarationKind::Struct { .. } => { + TypeDeclarationKind::Builtin(_) + | TypeDeclarationKind::Struct { .. } => { self.context.add_error( key.span.clone(), format!( @@ -1519,6 +1533,13 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { let type_declaration = self.context.hir.type_declarations[&type_].clone(); match &type_declaration.kind { + TypeDeclarationKind::Builtin(builtin_type) => { + self.context.add_error( + call.receiver.span.clone(), + format!("Can't instantiate builtin type `{builtin_type}` directly."), + ); + LoweredExpression::Error + } TypeDeclarationKind::Struct { fields } => { // Foo(bar, baz) self.lower_struct_creation( @@ -1527,7 +1548,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { type_arguments.as_deref(), &type_, &type_declaration.type_parameters, - fields.as_deref(), + Some(fields), ) } TypeDeclarationKind::Enum { .. } => { @@ -1566,9 +1587,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { } => match &receiver_type { Type::Named(named_type) => { let type_ = &self.context.hir.type_declarations[&named_type.name]; - if let TypeDeclarationKind::Struct { - fields: Some(fields), - } = &type_.kind + if let TypeDeclarationKind::Struct { fields } = &type_.kind && let Some(field) = fields.iter().find(|it| it.name == key.string) { return self.push_lowered( @@ -1617,6 +1636,16 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { LoweredExpression::NamedTypeReference(type_) => { let declaration = self.context.hir.type_declarations.get(&type_).unwrap(); match &declaration.kind { + TypeDeclarationKind::Builtin(_) => { + self.context.add_error( + key.span.clone(), + format!( + "Builtin type `{type_:?}` doesn't have a field `{}`", + key.string, + ), + ); + LoweredExpression::Error + } TypeDeclarationKind::Struct { .. } => { self.context.add_error( key.span.clone(), @@ -1715,6 +1744,13 @@ impl<'c, 'a> BodyBuilder<'c, 'a> { return LoweredExpression::Error; }; match &declaration.kind { + TypeDeclarationKind::Builtin(_) => { + self.context.add_error( + expression.span.clone(), + format!("Can't switch over builtin `{enum_:?}`"), + ); + return LoweredExpression::Error; + } TypeDeclarationKind::Struct { .. } => { self.context.add_error( expression.span.clone(), diff --git a/compiler_v4/src/hir.rs b/compiler_v4/src/hir.rs index 74bd2605a..227a0c7dc 100644 --- a/compiler_v4/src/hir.rs +++ b/compiler_v4/src/hir.rs @@ -75,21 +75,22 @@ impl ToText for Hir { .sorted_by_key(|(name, _)| *name) { match &declaration.kind { + TypeDeclarationKind::Builtin(_) => { + builder.push(format!("struct {name}")); + declaration.type_parameters.build_text(builder); + builder.push(" = builtin"); + } TypeDeclarationKind::Struct { fields } => { builder.push(format!("struct {name}")); declaration.type_parameters.build_text(builder); - if let Some(fields) = fields { - builder.push(" {"); - builder.push_children_custom_multiline(fields.iter(), |builder, it| { - builder.push(format!("{}: {},", it.name, it.type_)); - }); - if !fields.is_empty() { - builder.push_newline(); - } - builder.push("}"); - } else { - builder.push(" = builtin"); + builder.push(" {"); + builder.push_children_custom_multiline(fields.iter(), |builder, it| { + builder.push(format!("{}: {},", it.name, it.type_)); + }); + if !fields.is_empty() { + builder.push_newline(); } + builder.push("}"); } TypeDeclarationKind::Enum { variants } => { builder.push(format!("enum {name}")); @@ -150,15 +151,30 @@ pub struct TypeDeclaration { } #[derive(Clone, Debug, Eq, PartialEq)] pub enum TypeDeclarationKind { + Builtin(BuiltinType), Struct { - /// `None` if the struct is a builtin. - fields: Option>, + fields: Box<[StructField]>, }, Enum { variants: Box<[(Box, Option)]>, }, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum BuiltinType { + Int, + List(Box), + Text, +} +impl Display for BuiltinType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Int => write!(f, "Int"), + Self::List(item_type) => write!(f, "List[{item_type}]"), + Self::Text => write!(f, "Text"), + } + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct StructField { pub name: Box, pub type_: Type, diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 38af06c44..677b50153 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -277,32 +277,36 @@ impl<'h> Context<'h> { return mangled_name; }; match &declaration.kind { + hir::TypeDeclarationKind::Builtin(builtin_type) => { + entry.insert(None); + match builtin_type { + hir::BuiltinType::Int => { + mono::TypeDeclaration::Builtin(mono::BuiltinType::Int) + } + hir::BuiltinType::List(item_type) => mono::TypeDeclaration::Builtin( + mono::BuiltinType::List(self.lower_type(&item_type)), + ), + hir::BuiltinType::Text => { + mono::TypeDeclaration::Builtin(mono::BuiltinType::Text) + } + } + } hir::TypeDeclarationKind::Struct { fields } => { entry.insert(None); let environment = hir::Type::build_environment( &declaration.type_parameters, type_arguments, ); - if let Some(fields) = fields.as_ref() { - let fields = fields - .iter() - .map(|field| { - ( - field.name.clone(), - self.lower_type(&field.type_.substitute(&environment)), - ) - }) - .collect(); - mono::TypeDeclaration::Struct { fields } - } else { - mono::TypeDeclaration::Builtin { - name: name.clone(), - type_arguments: type_arguments - .iter() - .map(|it| self.lower_type(it)) - .collect(), - } - } + let fields = fields + .iter() + .map(|field| { + ( + field.name.clone(), + self.lower_type(&field.type_.substitute(&environment)), + ) + }) + .collect(); + mono::TypeDeclaration::Struct { fields } } hir::TypeDeclarationKind::Enum { variants } => { entry.insert(None); diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index 53d5c1afd..40bc454cd 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -40,18 +40,8 @@ impl ToText for Mono { builder.push("Type Declarations:"); builder.push_newline(); match declaration { - TypeDeclaration::Builtin { - name, - type_arguments, - } => { - builder.push(format!( - "builtin struct {name} = {name}{}", - if type_arguments.is_empty() { - String::new() - } else { - format!("[{}]", type_arguments.join(", ")) - } - )); + TypeDeclaration::Builtin(builtin_type) => { + builder.push(format!("builtin struct {name} = {builtin_type}")); } TypeDeclaration::Struct { fields } => { builder.push(format!("struct {name} {{")); @@ -120,10 +110,7 @@ impl ToText for Mono { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum TypeDeclaration { - Builtin { - name: Box, - type_arguments: Box<[Box]>, - }, + Builtin(BuiltinType), Struct { fields: Box<[(Box, Box)]>, }, @@ -136,6 +123,21 @@ pub enum TypeDeclaration { }, } #[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum BuiltinType { + Int, + List(Box), + Text, +} +impl Display for BuiltinType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Int => write!(f, "Int"), + Self::List(item_type) => write!(f, "List[{item_type}]"), + Self::Text => write!(f, "Text"), + } + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct EnumVariant { pub name: Box, pub value_type: Option>, diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index 9f01d9e89..afc1d76f4 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -1,8 +1,8 @@ use crate::{ hir::BuiltinFunction, mono::{ - Body, BodyOrBuiltin, Expression, ExpressionKind, Function, Id, Lambda, Mono, Parameter, - TypeDeclaration, + Body, BodyOrBuiltin, BuiltinType, Expression, ExpressionKind, Function, Id, Lambda, Mono, + Parameter, TypeDeclaration, }, }; use itertools::Itertools; @@ -76,25 +76,18 @@ impl<'h> Context<'h> { for (name, declaration) in &self.mono.type_declarations { self.push(format!("struct {name} {{\n")); match declaration { - TypeDeclaration::Builtin { - name, - type_arguments, - } => { - match name.as_ref() { - "Int" => { - assert!(type_arguments.is_empty()); + TypeDeclaration::Builtin(builtin_type) => { + match builtin_type { + BuiltinType::Int => { self.push("int64_t value;\n"); } - "List" => { - assert_eq!(type_arguments.len(), 1); + BuiltinType::List(item_type_) => { self.push("uint64_t length;\n"); - self.push(format!("{}** values;\n", type_arguments[0])); + self.push(format!("{item_type_}** values;\n")); } - "Text" => { - assert!(type_arguments.is_empty()); + BuiltinType::Text => { self.push("char* value;\n"); } - _ => panic!("Unknown builtin type: {name}"), } self.push("};\n"); } From dacb1e1d7464d31dc7759632c5c469634258c980 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 17:58:39 +0100 Subject: [PATCH 07/17] Lay out memory --- compiler_v4/src/hir_to_mono.rs | 16 ++- compiler_v4/src/main.rs | 4 +- compiler_v4/src/memory_layout.rs | 227 +++++++++++++++++++++++++++++++ compiler_v4/src/mono.rs | 2 + 4 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 compiler_v4/src/memory_layout.rs diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 677b50153..0dc663e51 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -2,6 +2,7 @@ use crate::{ ast_to_hir::TypeUnifier, hir::{self, BuiltinFunction, Hir, NamedType, ParameterType, Type}, id::IdGenerator, + memory_layout::lay_out_memory, mono::{self, Mono}, type_solver::{goals::SolverSolution, values::SolverVariable}, utils::HashMapExtension, @@ -33,12 +34,16 @@ impl<'h> Context<'h> { }; let main_function = context.lower_function(hir.main_function_id, &FxHashMap::default()); context.lower_function(BuiltinFunction::Panic.id(), &FxHashMap::default()); + + let type_declarations = context + .type_declarations + .into_iter() + .map(|(name, declaration)| (name, declaration.unwrap())) + .collect(); + let memory_layout = lay_out_memory(&type_declarations); + Mono { - type_declarations: context - .type_declarations - .into_iter() - .map(|(name, declaration)| (name, declaration.unwrap())) - .collect(), + type_declarations, assignments: context .assignments .into_iter() @@ -52,6 +57,7 @@ impl<'h> Context<'h> { .into_iter() .map(|(name, function)| (name, function.unwrap())) .collect(), + memory_layouts, main_function, } } diff --git a/compiler_v4/src/main.rs b/compiler_v4/src/main.rs index 875d8ca2e..a56a68e89 100644 --- a/compiler_v4/src/main.rs +++ b/compiler_v4/src/main.rs @@ -3,7 +3,8 @@ box_patterns, if_let_guard, let_chains, - try_blocks + try_blocks, + unsigned_is_multiple_of )] #![warn(clippy::nursery, clippy::pedantic, unused_crate_dependencies)] #![allow( @@ -47,6 +48,7 @@ mod error; mod hir; mod hir_to_mono; mod id; +mod memory_layout; mod mono; mod mono_to_c; mod position; diff --git a/compiler_v4/src/memory_layout.rs b/compiler_v4/src/memory_layout.rs new file mode 100644 index 000000000..36b62cf48 --- /dev/null +++ b/compiler_v4/src/memory_layout.rs @@ -0,0 +1,227 @@ +use crate::{ + mono::{BuiltinType, TypeDeclaration}, + utils::HashSetExtension, +}; +use itertools::Itertools; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::{cmp::Reverse, collections::hash_map::Entry}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TypeLayout { + layout: Layout, + kind: TypeLayoutKind, +} +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TypeLayoutKind { + Builtin, + Struct { + field_offsets: FxHashMap, usize>, + }, + Enum { + tag_offset: usize, + boxed_variants: FxHashSet>, + }, +} + +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Layout { + size: usize, + alignment: Alignment, +} +impl Layout { + pub const POINTER: Self = Self::new(8, Alignment::_8); + + #[must_use] + pub const fn new(size: usize, alignment: Alignment) -> Self { + assert!(size.is_multiple_of(alignment.get())); + Self { size, alignment } + } +} +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum Alignment { + #[default] + _1, + _2, + _4, + _8, +} +impl Alignment { + #[must_use] + const fn get(self) -> usize { + match self { + Alignment::_1 => 1, + Alignment::_2 => 2, + Alignment::_4 => 4, + Alignment::_8 => 8, + } + } +} + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct AggregateLayout { + layout: Layout, + part_offsets: Box<[usize]>, +} + +pub fn lay_out_memory( + type_declarations: &FxHashMap, TypeDeclaration>, +) -> FxHashMap, TypeLayout> { + let mut context = Context::new(type_declarations); + for type_ in type_declarations.keys() { + context.lay_out(type_); + } + context + .memory_layouts + .into_iter() + .map(|(type_, layout)| (type_, layout.unwrap())) + .collect() +} +struct Context<'m> { + type_declarations: &'m FxHashMap, TypeDeclaration>, + memory_layouts: FxHashMap, Option>, +} +impl<'m> Context<'m> { + fn new(type_declarations: &'m FxHashMap, TypeDeclaration>) -> Self { + Self { + type_declarations, + memory_layouts: FxHashMap::default(), + } + } + + /// `None` means a recursive struct type that would have infinite size. + fn lay_out(&mut self, type_: &str) -> Option { + match self.memory_layouts.entry(type_.into()) { + Entry::Occupied(entry) => { + return entry.get().map(|it| it.layout); + } + Entry::Vacant(entry) => { + entry.insert(None); + } + } + + let declaration = &self.type_declarations[type_]; + let type_layout = match declaration { + TypeDeclaration::Builtin(builtin_type) => { + let layout = match builtin_type { + BuiltinType::Int => Layout::new(8, Alignment::_8), + BuiltinType::List(_) => Layout::new(16, Alignment::_8), + BuiltinType::Text => Layout::new(8, Alignment::_8), + }; + TypeLayout { + layout, + kind: TypeLayoutKind::Builtin, + } + } + TypeDeclaration::Struct { fields } => { + let field_layouts = fields + .iter() + .map(|(name, type_)| try { (name, self.lay_out(type_)?) }) + .collect::>>()?; + let parts = field_layouts + .iter() + .map(|(_, layout)| *layout) + .collect::>(); + let aggregate_layout = Self::lay_out_aggregate(&parts); + TypeLayout { + layout: aggregate_layout.layout, + kind: TypeLayoutKind::Struct { + field_offsets: field_layouts + .iter() + .zip_eq(aggregate_layout.part_offsets.iter()) + .map(|((name, _), offset)| ((*name).clone(), *offset)) + .collect(), + }, + } + } + TypeDeclaration::Enum { variants } => { + if variants.len() > 256 { + todo!("support enums with more than 256 variants") + } + + let mut size = 0; + let mut alignment = Alignment::default(); + let mut boxed_variants = FxHashSet::default(); + for variant in variants.iter() { + if let Some(value_type) = variant.value_type.as_ref() { + let mut layout = self.lay_out(value_type)?; + if self.is_field_recursive(type_, value_type) { + layout = Layout::POINTER; + boxed_variants.force_insert(variant.name.clone()); + } + size = size.max(layout.size); + alignment = alignment.max(layout.alignment); + } + } + let tag_offset = size; + size += 1; + TypeLayout { + layout: Layout { size, alignment }, + kind: TypeLayoutKind::Enum { + tag_offset, + boxed_variants, + }, + } + } + TypeDeclaration::Function { .. } => TypeLayout { + layout: Layout::new(16, Alignment::_8), + kind: TypeLayoutKind::Builtin, + }, + }; + let layout = type_layout.layout; + self.memory_layouts + .insert(type_.into(), Some(type_layout)) + .unwrap(); + Some(layout) + } + fn is_field_recursive(&self, outer_type: &str, field_type: &str) -> bool { + if outer_type == field_type { + return true; + } + + match &self.type_declarations[field_type] { + TypeDeclaration::Builtin(_) => false, + TypeDeclaration::Struct { fields } => fields + .iter() + .any(|(_, field_type)| self.is_field_recursive(outer_type, field_type)), + TypeDeclaration::Enum { variants } => variants.iter().any(|variant| { + variant + .value_type + .iter() + .any(|field_type| self.is_field_recursive(outer_type, field_type)) + }), + TypeDeclaration::Function { .. } => false, + } + } + + fn lay_out_aggregate(parts: &[Layout]) -> AggregateLayout { + if parts.is_empty() { + return AggregateLayout::default(); + } + + let parts = parts + .iter() + .enumerate() + .sorted_by_key(|(index, layout)| { + (Reverse(layout.alignment), Reverse(layout.size), *index) + }) + .collect_vec(); + let alignment = parts.first().unwrap().1.alignment; + + let mut part_offsets = Box::<[usize]>::new_uninit_slice(parts.len()); + let mut offset = 0usize; + for (index, layout) in parts { + offset = offset.next_multiple_of(layout.alignment.get()); + part_offsets[index].write(offset); + offset += layout.size; + } + + let part_offsets = unsafe { part_offsets.assume_init() }; + AggregateLayout { + layout: Layout { + size: offset, + alignment, + }, + part_offsets, + } + } +} diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index 40bc454cd..ac615e65d 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -1,6 +1,7 @@ use crate::{ hir::BuiltinFunction, impl_countable_id, + memory_layout::TypeLayout, to_text::{TextBuilder, ToText}, }; use derive_more::Deref; @@ -25,6 +26,7 @@ impl ToText for Id { #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Mono { pub type_declarations: FxHashMap, TypeDeclaration>, + pub memory_layouts: FxHashMap, TypeLayout>, pub assignments: FxHashMap, Assignment>, pub assignment_initialization_order: Box<[Box]>, pub functions: FxHashMap, Function>, From 609e19356e1d9781d20c5887cd902a53932a1484 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 19:12:35 +0100 Subject: [PATCH 08/17] Fix list type lowering --- compiler_v4/src/ast_to_hir.rs | 4 +--- compiler_v4/src/hir.rs | 4 ++-- compiler_v4/src/hir_to_mono.rs | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler_v4/src/ast_to_hir.rs b/compiler_v4/src/ast_to_hir.rs index f413cf5a9..8c36e6a58 100644 --- a/compiler_v4/src/ast_to_hir.rs +++ b/compiler_v4/src/ast_to_hir.rs @@ -504,9 +504,7 @@ impl<'a> Context<'a> { let kind = match &struct_type.kind { AstStructKind::Builtin { .. } => TypeDeclarationKind::Builtin(match &*name.string { "Int" => BuiltinType::Int, - "List" => BuiltinType::List(Box::new( - type_parameters.iter().next().unwrap().type_().into(), - )), + "List" => BuiltinType::List, "Text" => BuiltinType::Text, _ => { self.add_error( diff --git a/compiler_v4/src/hir.rs b/compiler_v4/src/hir.rs index 227a0c7dc..b2e1f8464 100644 --- a/compiler_v4/src/hir.rs +++ b/compiler_v4/src/hir.rs @@ -162,14 +162,14 @@ pub enum TypeDeclarationKind { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum BuiltinType { Int, - List(Box), + List, Text, } impl Display for BuiltinType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Self::Int => write!(f, "Int"), - Self::List(item_type) => write!(f, "List[{item_type}]"), + Self::List => write!(f, "List[T]"), Self::Text => write!(f, "Text"), } } diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 0dc663e51..1dd132031 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -289,8 +289,8 @@ impl<'h> Context<'h> { hir::BuiltinType::Int => { mono::TypeDeclaration::Builtin(mono::BuiltinType::Int) } - hir::BuiltinType::List(item_type) => mono::TypeDeclaration::Builtin( - mono::BuiltinType::List(self.lower_type(&item_type)), + hir::BuiltinType::List => mono::TypeDeclaration::Builtin( + mono::BuiltinType::List(self.lower_type(&type_arguments[0])), ), hir::BuiltinType::Text => { mono::TypeDeclaration::Builtin(mono::BuiltinType::Text) From 07fdb11a8bcbfd9136e98fb8609618fd475e51f2 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 19:12:40 +0100 Subject: [PATCH 09/17] Fix typo --- compiler_v4/src/hir_to_mono.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 1dd132031..c4cccb0d2 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -40,7 +40,7 @@ impl<'h> Context<'h> { .into_iter() .map(|(name, declaration)| (name, declaration.unwrap())) .collect(); - let memory_layout = lay_out_memory(&type_declarations); + let memory_layouts = lay_out_memory(&type_declarations); Mono { type_declarations, From de7d10b3f5862de40b5e4fd7c245e58f78cd4459 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 19:12:51 +0100 Subject: [PATCH 10/17] Fix linter complaints --- Cargo.toml | 22 +++++++++++----------- compiler_v4/src/memory_layout.rs | 16 ++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fff06d9d5..7f8b863a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,21 @@ [workspace] resolver = "2" members = [ - "compiler_v4", - "compiler/backend_inkwell", - "compiler/cli", - "compiler/formatter", - "compiler/formatter/fuzz", - "compiler/frontend", - "compiler/fuzzer", - "compiler/language_server", - "compiler/vm", - "compiler/vm/fuzz", + "compiler_v4", + "compiler/backend_inkwell", + "compiler/cli", + "compiler/formatter", + "compiler/formatter/fuzz", + "compiler/frontend", + "compiler/fuzzer", + "compiler/language_server", + "compiler/vm", + "compiler/vm/fuzz", ] [workspace.package] edition = "2021" -rust-version = "1.80.0" +rust-version = "1.82.0" [profile.release] # This adds file and line number information to backtraces while only increasing diff --git a/compiler_v4/src/memory_layout.rs b/compiler_v4/src/memory_layout.rs index 36b62cf48..d40103ded 100644 --- a/compiler_v4/src/memory_layout.rs +++ b/compiler_v4/src/memory_layout.rs @@ -8,8 +8,8 @@ use std::{cmp::Reverse, collections::hash_map::Entry}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct TypeLayout { - layout: Layout, - kind: TypeLayoutKind, + pub layout: Layout, + pub kind: TypeLayoutKind, } #[derive(Clone, Debug, Eq, PartialEq)] pub enum TypeLayoutKind { @@ -49,10 +49,10 @@ impl Alignment { #[must_use] const fn get(self) -> usize { match self { - Alignment::_1 => 1, - Alignment::_2 => 2, - Alignment::_4 => 4, - Alignment::_8 => 8, + Self::_1 => 1, + Self::_2 => 2, + Self::_4 => 4, + Self::_8 => 8, } } } @@ -92,7 +92,7 @@ impl<'m> Context<'m> { fn lay_out(&mut self, type_: &str) -> Option { match self.memory_layouts.entry(type_.into()) { Entry::Occupied(entry) => { - return entry.get().map(|it| it.layout); + return entry.get().as_ref().map(|it| it.layout); } Entry::Vacant(entry) => { entry.insert(None); @@ -141,7 +141,7 @@ impl<'m> Context<'m> { let mut size = 0; let mut alignment = Alignment::default(); let mut boxed_variants = FxHashSet::default(); - for variant in variants.iter() { + for variant in variants { if let Some(value_type) = variant.value_type.as_ref() { let mut layout = self.lay_out(value_type)?; if self.is_field_recursive(type_, value_type) { From f9d17f00a91338fb7c5498392483e9b08758eae9 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 19:14:52 +0100 Subject: [PATCH 11/17] Avoid boxing where possible --- compiler_v4/src/mono_to_c.rs | 582 ++++++++++++++++++----------------- 1 file changed, 300 insertions(+), 282 deletions(-) diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index afc1d76f4..7506b3663 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -1,11 +1,13 @@ use crate::{ hir::BuiltinFunction, + memory_layout::TypeLayoutKind, mono::{ Body, BodyOrBuiltin, BuiltinType, Expression, ExpressionKind, Function, Id, Lambda, Mono, Parameter, TypeDeclaration, }, }; use itertools::Itertools; +use rustc_hash::FxHashSet; pub fn mono_to_c(mono: &Mono) -> String { let mut context = Context::new(mono); @@ -61,10 +63,7 @@ impl<'h> Context<'h> { for name in &self.mono.assignment_initialization_order { self.push(format!("{name}$init();\n")); } - self.push(format!( - "return {}()->value;\n}}\n", - self.mono.main_function, - )); + self.push(format!("return {}().value;\n}}\n", self.mono.main_function,)); } fn lower_type_forward_declarations(&mut self) { @@ -73,6 +72,7 @@ impl<'h> Context<'h> { } } fn lower_type_declarations(&mut self) { + // FIXME: topological sort for (name, declaration) in &self.mono.type_declarations { self.push(format!("struct {name} {{\n")); match declaration { @@ -83,7 +83,7 @@ impl<'h> Context<'h> { } BuiltinType::List(item_type_) => { self.push("uint64_t length;\n"); - self.push(format!("{item_type_}** values;\n")); + self.push(format!("{item_type_}* values;\n")); } BuiltinType::Text => { self.push("char* value;\n"); @@ -93,7 +93,7 @@ impl<'h> Context<'h> { } TypeDeclaration::Struct { fields } => { for (name, type_) in &**fields { - self.push(format!("{type_}* {name}; ")); + self.push(format!("{type_} {name}; ")); } self.push("};\n"); } @@ -107,9 +107,18 @@ impl<'h> Context<'h> { } self.push("union {"); + let boxed_variants = self.get_boxed_variants(name).clone(); for variant in &**variants { if let Some(value_type) = &variant.value_type { - self.push(format!("{value_type}* {};", variant.name)); + self.push(format!( + "{value_type}{} {};", + if boxed_variants.contains(&*variant.name) { + "*" + } else { + "" + }, + variant.name, + )); } } self.push("} value;\n};\n"); @@ -119,9 +128,9 @@ impl<'h> Context<'h> { return_type, } => { self.push("void* closure;\n"); - self.push(format!("{return_type}* (*function)(void*")); + self.push(format!("{return_type} (*function)(void*")); for parameter_type in &**parameter_types { - self.push(format!(", {parameter_type}*")); + self.push(format!(", {parameter_type}")); } self.push(");\n"); self.push("};\n"); @@ -129,11 +138,18 @@ impl<'h> Context<'h> { } } } + fn get_boxed_variants(&self, enum_type: &str) -> &FxHashSet> { + let TypeLayoutKind::Enum { boxed_variants, .. } = &self.mono.memory_layouts[enum_type].kind + else { + panic!("Not an enum type: `{enum_type}`"); + }; + boxed_variants + } fn lower_assignment_declarations(&mut self) { for (name, assignment) in &self.mono.assignments { self.lower_lambda_declarations_in(name, &assignment.body); - self.push(format!("{}* {name};\n", &assignment.type_)); + self.push(format!("{} {name};\n", &assignment.type_)); } } fn lower_assignment_definitions(&mut self) { @@ -192,7 +208,7 @@ impl<'h> Context<'h> { self.push(format!("struct {declaration_name}$lambda{id}_closure {{")); for (id, type_) in &closure { - self.push(format!("{type_}* {id}; ")); + self.push(format!("{type_} {id}; ")); } self.push("};\n"); @@ -202,29 +218,29 @@ impl<'h> Context<'h> { "{declaration_name}$lambda{id}_closure* closure = raw_closure;\n" )); for (id, type_) in &closure { - self.push(format!("{type_}* {id} = closure->{id};\n")); + self.push(format!("{type_} {id} = closure->{id};\n")); } self.lower_body(declaration_name, &lambda.body); self.push("}\n"); }); } fn lower_function_signature(&mut self, name: &str, function: &Function) { - self.push(format!("{}* {name}(", &function.return_type)); + self.push(format!("{} {name}(", &function.return_type)); for (index, parameter) in function.parameters.iter().enumerate() { if index > 0 { self.push(", "); } - self.push(format!("{}* {}", ¶meter.type_, parameter.id)); + self.push(format!("{} {}", ¶meter.type_, parameter.id)); } self.push(")"); } fn lower_lambda_signature(&mut self, declaration_name: &str, id: Id, lambda: &Lambda) { self.push(format!( - "{}* {declaration_name}$lambda{id}_function(void* raw_closure", + "{} {declaration_name}$lambda{id}_function(void* raw_closure", &lambda.body.return_type() )); for parameter in &lambda.parameters { - self.push(format!(", {}* {}", ¶meter.type_, parameter.id)); + self.push(format!(", {} {}", ¶meter.type_, parameter.id)); } self.push(")"); } @@ -264,157 +280,144 @@ impl<'h> Context<'h> { match builtin_function { BuiltinFunction::IntAdd => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {a}->value + {b}->value; - return result_pointer;", + Int result = {{.value = {a}.value + {b}.value}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::IntBitwiseAnd => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {a}->value & {b}->value; - return result_pointer;", + Int result = {{.value = {a}.value & {b}.value}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::IntBitwiseOr => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {a}->value | {b}->value; - return result_pointer;", + Int result = {{.value = {a}.value | {b}.value}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::IntBitwiseXor => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {a}->value ^ {b}->value; - return result_pointer;", + Int result = {{.value = {a}.value ^ {b}.value}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::IntCompareTo => self.push(format!( "\ - Ordering* result_pointer = malloc(sizeof(Ordering)); - result_pointer->variant = {a}->value < {b}->value ? Ordering_less - : {a}->value == {b}->value ? Ordering_equal - : Ordering_greater; - return result_pointer;", + Ordering result = {{.variant = {a}.value < {b}.value ? Ordering_less + : {a}.value == {b}.value ? Ordering_equal + : Ordering_greater}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::IntDivideTruncating => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {dividend}->value / {divisor}->value; - return result_pointer;", + Int result = {{.value = {dividend}.value / {divisor}.value}}; + return result;", dividend = function.parameters[0].id, divisor = function.parameters[1].id, )), BuiltinFunction::IntMultiply => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {factorA}->value * {factorB}->value; - return result_pointer;", + Int result = {{.value = {factorA}.value * {factorB}.value}}; + return result;", factorA = function.parameters[0].id, factorB = function.parameters[1].id, )), BuiltinFunction::IntParse => self.push(format!( "\ - {return_type}* result_pointer = malloc(sizeof({return_type})); + {return_type} result; char *end_pointer; errno = 0; - uint64_t value = strtol({text}->value, &end_pointer, 10); + uint64_t value = strtol({text}.value, &end_pointer, 10); if (errno == ERANGE) {{ - result_pointer->variant = {return_type}_error; - result_pointer->value.error = malloc(sizeof(Text)); - result_pointer->value.error->value = \"Value is out of range.\"; - }} else if (end_pointer == {text}->value) {{ - result_pointer->variant = {return_type}_error; - result_pointer->value.error = malloc(sizeof(Text)); - result_pointer->value.error->value = \"Text is empty.\"; + result = {{ + .variant = {return_type}_error, + .value.error = {{.value = \"Value is out of range.\"}}, + }}; + }} else if (end_pointer == {text}.value) {{ + result = {{ + .variant = {return_type}_error, + .value.error = {{.value = \"Text is empty.\"}}, + }}; }} else if (*end_pointer != '\\0') {{ char* message_format = \"Non-numeric character \\\"%c\\\" at index %ld.\"; - int length = snprintf(NULL, 0, message_format, *end_pointer, end_pointer - {text}->value); + int length = snprintf(NULL, 0, message_format, *end_pointer, end_pointer - {text}.value); char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, *end_pointer, end_pointer - {text}->value); - - result_pointer->variant = {return_type}_error; - result_pointer->value.error = malloc(sizeof(Text)); - result_pointer->value.error->value = message; + snprintf(message, length + 1, message_format, *end_pointer, end_pointer - {text}.value); + result = {{ + .variant = {return_type}_error, + .value.error = {{.value = message}}, + }}; }} else {{ - result_pointer->variant = {return_type}_ok; - result_pointer->value.ok = malloc(sizeof(Int)); - result_pointer->value.ok ->value = value; + result = {{ + .variant = {return_type}_ok, + .value.ok = {{.value = value}}, + }}; }} - return result_pointer;", + return result;", text = function.parameters[0].id, return_type = function.return_type, )), BuiltinFunction::IntRemainder => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {dividend}->value % {divisor}->value; - return result_pointer;", + Int result = {{.value = {dividend}.value % {divisor}.value}}; + return result;", dividend = function.parameters[0].id, divisor = function.parameters[1].id, )), BuiltinFunction::IntShiftLeft => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {value}->value << {amount}->value; - return result_pointer;", + Int result = {{.value = {value}.value << {amount}.value}}; + return result;", value = function.parameters[0].id, amount = function.parameters[1].id, )), BuiltinFunction::IntShiftRight => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {value}->value >> {amount}->value; - return result_pointer;", + Int result = {{.value = {value}.value >> {amount}.value}}; + return result;", value = function.parameters[0].id, amount = function.parameters[1].id, )), BuiltinFunction::IntSubtract => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {minuend}->value - {subtrahend}->value; - return result_pointer;", + Int result = {{.value = {minuend}.value - {subtrahend}.value}}; + return result;", minuend = function.parameters[0].id, subtrahend = function.parameters[1].id, )), BuiltinFunction::IntToText => self.push(format!( "\ - int length = snprintf(NULL, 0, \"%ld\", {int}->value); - char* result = malloc(length + 1); - snprintf(result, length + 1, \"%ld\", {int}->value); - - Text* result_pointer = malloc(sizeof(Text)); - result_pointer->value = result; - return result_pointer;", + int length = snprintf(NULL, 0, \"%ld\", {int}.value); + Text result = {{.value = malloc(length + 1)}}; + snprintf(result.value, length + 1, \"%ld\", {int}.value); + return result;", int = function.parameters[0].id, )), BuiltinFunction::ListFilled => self.push(format!( "\ - if ({length}->value < 0) {{ + if ({length}.value < 0) {{ char* message_format = \"List length must not be negative; was %ld.\"; - int length = snprintf(NULL, 0, message_format, {length}->value); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {length}->value); - - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {length}.value); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {length}.value); + builtinPanic$$Text(message); }} - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = {length}->value; - result_pointer->values = malloc({length}->value * sizeof({item_type}*)); - for (uint64_t i = 0; i < {length}->value; i++) {{ - result_pointer->values[i] = {item}; + {list_type} result = {{ + length = {length}.value, + values = malloc({length}.value * sizeof({item_type})), + }}; + for (uint64_t i = 0; i < {length}.value; i++) {{ + result.values[i] = {item}; }} - return result_pointer;", + return result;", item_type = substitutions["T"], list_type = function.return_type, length = function.parameters[0].id, @@ -422,26 +425,23 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListGenerate => self.push(format!( "\ - if ({length}->value < 0) {{ + if ({length}.value < 0) {{ char* message_format = \"List length must not be negative; was %ld.\"; - int length = snprintf(NULL, 0, message_format, {length}->value); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {length}->value); - - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {length}.value); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {length}.value); + builtinPanic$$Text(message); }} - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = {length}->value; - result_pointer->values = malloc({length}->value * sizeof({item_type}*)); - for (uint64_t i = 0; i < {length}->value; i++) {{ - Int* index = malloc(sizeof(Int)); - index->value = i; - result_pointer->values[i] = {item_getter}->function({item_getter}->closure, index); + {list_type} result = {{ + .length = {length}.value, + .values = malloc({length}.value * sizeof({item_type}*)), + }}; + for (uint64_t i = 0; i < {length}.value; i++) {{ + Int index = {{.value = i}}; + result.values[i] = {item_getter}.function({item_getter}.closure, index); }} - return result_pointer;", + return result;", item_type = substitutions["T"], list_type = function.return_type, length = function.parameters[0].id, @@ -449,38 +449,39 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListGet => self.push(format!( "\ - {return_type}* result_pointer = malloc(sizeof({return_type})); - if (0 <= {index}->value && {index}->value < {list}->length) {{ - result_pointer->variant = {return_type}_some; - result_pointer->value.some = {list}->values[{index}->value]; + {return_type} result = malloc(sizeof({return_type})); + if (0 <= {index}.value && {index}.value < {list}.length) {{ + result = {{ + .variant = {return_type}_some, + .value.some = {list}.values[{index}.value], + }}; }} else {{ - result_pointer->variant = {return_type}_none; + result = {{.variant = {return_type}_none}}; }} - return result_pointer;", + return result;", return_type = function.return_type, list = function.parameters[0].id, index = function.parameters[1].id, )), BuiltinFunction::ListInsert => self.push(format!( "\ - if (0 > {index}->value || {index}->value > {list}->length) {{ + if (0 > {index}.value || {index}.value > {list}.length) {{ char* message_format = \"Index out of bounds: Tried inserting at index %ld in list of length %ld.\"; - int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {index}->value, {list}->length); - - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {index}.value, {list}.length); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {index}.value, {list}.length); + builtinPanic$$Text(message); }} - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = {list}->length + 1; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); - memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type}*)); - result_pointer->values[{index}->value] = {item}; - memcpy(result_pointer->values + {index}->value + 1, {list}->values + {index}->value, ({list}->length - {index}->value) * sizeof({item_type})); - return result_pointer;", + uint64_t length = {list}.length + 1; + {list_type} result = {{ + .length = length, + .values = malloc(length * sizeof({item_type})), + }}; + memcpy(result.values, {list}.values, {index}.value * sizeof({item_type}*)); + result.values[{index}.value] = {item}; + memcpy(result.values + {index}.value + 1, {list}.values + {index}.value, ({list}.length - {index}.value) * sizeof({item_type})); + return result;", item_type = substitutions["T"], list_type = function.return_type, list = function.parameters[0].id, @@ -489,38 +490,40 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListLength => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {list}->length; - return result_pointer;", + Int result = {{.value = {list}.length}}; + return result;", list = function.parameters[0].id, )), BuiltinFunction::ListOf0 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 0; - result_pointer->values = NULL; - return result_pointer;", + {list_type} result = {{ + .length = 0, + .values = NULL, + }}; + return result;", list_type = function.return_type, )), BuiltinFunction::ListOf1 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 1; - result_pointer->values = malloc(sizeof({item_type}*)); - result_pointer->values[0] = {item0}; - return result_pointer;", + {list_type} result = {{ + .length = 1, + .values = malloc(sizeof({item_type})), + }}; + result.values[0] = {item0}; + return result;", item_type = substitutions["T"], list_type = function.return_type, item0 = function.parameters[0].id, )), BuiltinFunction::ListOf2 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 2; - result_pointer->values = malloc(2 * sizeof({item_type}*)); - result_pointer->values[0] = {item0}; - result_pointer->values[1] = {item1}; - return result_pointer;", + {list_type} result = {{ + .length = 2, + .values = malloc(2 * sizeof({item_type})), + }}; + result.values[0] = {item0}; + result.values[1] = {item1}; + return result;", item_type = substitutions["T"], list_type = function.return_type, item0 = function.parameters[0].id, @@ -528,13 +531,14 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListOf3 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 3; - result_pointer->values = malloc(3 * sizeof({item_type}*)); - result_pointer->values[0] = {item0}; - result_pointer->values[1] = {item1}; - result_pointer->values[2] = {item2}; - return result_pointer;", + {list_type} result = {{ + .length = 3, + .values = malloc(3 * sizeof({item_type})), + }}; + result.values[0] = {item0}; + result.values[1] = {item1}; + result.values[2] = {item2}; + return result;", item_type = substitutions["T"], list_type = function.return_type, item0 = function.parameters[0].id, @@ -543,14 +547,15 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListOf4 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 4; - result_pointer->values = malloc(4 * sizeof({item_type}*)); - result_pointer->values[0] = {item0}; - result_pointer->values[1] = {item1}; - result_pointer->values[2] = {item2}; - result_pointer->values[3] = {item3}; - return result_pointer;", + {list_type} result = {{ + .length = 4, + .values = malloc(4 * sizeof({item_type})), + }}; + result.values[0] = {item0}; + result.values[1] = {item1}; + result.values[2] = {item2}; + result.values[3] = {item3}; + return result;", item_type = substitutions["T"], list_type = function.return_type, item0 = function.parameters[0].id, @@ -560,15 +565,16 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListOf5 => self.push(format!( "\ - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = 5; - result_pointer->values = malloc(5 * sizeof({item_type}*)); - result_pointer->values[0] = {item0}; - result_pointer->values[1] = {item1}; - result_pointer->values[2] = {item2}; - result_pointer->values[3] = {item3}; - result_pointer->values[4] = {item4}; - return result_pointer;", + {list_type} result = {{ + .length = 5, + .values = malloc(5 * sizeof({item_type})), + }}; + result.values[0] = {item0}; + result.values[1] = {item1}; + result.values[2] = {item2}; + result.values[3] = {item3}; + result.values[4] = {item4}; + return result;", item_type = substitutions["T"], list_type = function.return_type, item0 = function.parameters[0].id, @@ -579,23 +585,22 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListRemoveAt => self.push(format!( "\ - if (0 > {index}->value || {index}->value >= {list}->length) {{ + if (0 > {index}.value || {index}.value >= {list}.length) {{ char* message_format = \"Index out of bounds: Tried removing item at index %ld from list of length %ld.\"; - int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {index}->value, {list}->length); - - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {index}.value, {list}.length); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {index}.value, {list}.length); + builtinPanic$$Text(message); }} - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = {list}->length - 1; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); - memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type}*)); - memcpy(result_pointer->values + {index}->value, {list}->values + {index}->value + 1, ({list}->length - {index}->value - 1) * sizeof({item_type}*)); - return result_pointer;", + uint64_t length = {list}.length - 1; + {list_type} result = {{ + .length = length, + .values = malloc(length * sizeof({item_type})), + }}; + memcpy(result.values, {list}.values, {index}.value * sizeof({item_type}*)); + memcpy(result.values + {index}.value, {list}.values + {index}.value + 1, ({list}.length - {index}.value - 1) * sizeof({item_type}*)); + return result;", item_type = substitutions["T"], list_type = function.return_type, list = function.parameters[0].id, @@ -603,23 +608,21 @@ impl<'h> Context<'h> { )), BuiltinFunction::ListReplace => self.push(format!( "\ - if (0 > {index}->value || {index}->value >= {list}->length) {{ + if (0 > {index}.value || {index}.value >= {list}.length) {{ char* message_format = \"Index out of bounds: Tried replacing index %ld in list of length %ld.\"; - int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {index}->value, {list}->length); - - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {index}.value, {list}.length); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {index}.value, {list}.length); + builtinPanic$$Text(message); }} - {list_type}* result_pointer = malloc(sizeof({list_type})); - result_pointer->length = {list}->length; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); - memcpy(result_pointer->values, {list}->values, {list}->length * sizeof({item_type}*)); - result_pointer->values[{index}->value] = {new_item}; - return result_pointer;", + {list_type} result = {{ + .length = {list}.length, + .values = malloc({list}.length * sizeof({item_type})), + }}; + memcpy(result.values, {list}.values, {list}.length * sizeof({item_type}*)); + result.values[{index}.value] = {new_item}; + return result;", item_type = substitutions["T"], list_type = function.return_type, list = function.parameters[0].id, @@ -629,7 +632,7 @@ impl<'h> Context<'h> { BuiltinFunction::Panic => { self.push(format!( "\ - fputs({}->value, stderr); + fputs({}.value, stderr); exit(1);", function.parameters[0].id, )); @@ -637,90 +640,80 @@ impl<'h> Context<'h> { BuiltinFunction::Print => { self.push(format!( "\ - puts({}->value); - Nothing *_1 = malloc(sizeof(Nothing)); + puts({}.value); + Nothing _1 = {{}}; return _1;", function.parameters[0].id, )); } BuiltinFunction::TextCompareTo => self.push(format!( "\ - int raw_result = strcmp({a}->value, {b}->value); - Ordering* result_pointer = malloc(sizeof(Ordering)); - result_pointer->variant = raw_result < 0 ? Ordering_less - : raw_result == 0 ? Ordering_equal - : Ordering_greater; - return result_pointer;", + int raw_result = strcmp({a}.value, {b}.value); + Ordering result = {{.variant = raw_result < 0 ? Ordering_less + : raw_result == 0 ? Ordering_equal + : Ordering_greater}}; + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::TextConcat => self.push(format!( "\ - size_t lengthA = strlen({a}->value);\n\ - size_t lengthB = strlen({b}->value);\n\ - char* result = malloc(lengthA + lengthB + 1);\n\ - memcpy(result, {a}->value, lengthA);\n\ - memcpy(result + lengthA, {b}->value, lengthB + 1);\n\ - Text* result_pointer = malloc(sizeof(Text)); - result_pointer->value = result; - return result_pointer;", + size_t lengthA = strlen({a}.value);\n\ + size_t lengthB = strlen({b}.value);\n\ + Text result = {{.value = malloc(lengthA + lengthB + 1)}};\n\ + memcpy(result.value, {a}.value, lengthA);\n\ + memcpy(result.value + lengthA, {b}.value, lengthB + 1);\n\ + return result;", a = function.parameters[0].id, b = function.parameters[1].id, )), BuiltinFunction::TextGetRange => self.push(format!( "\ - size_t text_length = strlen({text}->value); - if (0 > {start_inclusive}->value || {start_inclusive}->value > text_length - || 0 > {end_exclusive}->value || {end_exclusive}->value > text_length) {{ + size_t text_length = strlen({text}.value); + if (0 > {start_inclusive}.value || {start_inclusive}.value > text_length + || 0 > {end_exclusive}.value || {end_exclusive}.value > text_length) {{ char* message_format = \"Index out of bounds: Tried getting range %ld..%ld from text that is only %ld long.\"; - int length = snprintf(NULL, 0, message_format, {start_inclusive}->value, {end_exclusive}->value, text_length); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {start_inclusive}->value, {end_exclusive}->value, text_length); - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); - }} else if ({start_inclusive}->value > {end_exclusive}->value) {{ + int length = snprintf(NULL, 0, message_format, {start_inclusive}.value, {end_exclusive}.value, text_length); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {start_inclusive}.value, {end_exclusive}.value, text_length); + builtinPanic$$Text(message); + }} else if ({start_inclusive}.value > {end_exclusive}.value) {{ char* message_format = \"Invalid range %ld..%ld: `start_inclusive` must be less than or equal to `end_exclusive`.\"; - int length = snprintf(NULL, 0, message_format, {start_inclusive}->value, {end_exclusive}->value); - char *message = malloc(length + 1); - snprintf(message, length + 1, message_format, {start_inclusive}->value, {end_exclusive}->value); - Text *message_pointer = malloc(sizeof(Text)); - message_pointer->value = message; - builtinPanic$$Text(message_pointer); + int length = snprintf(NULL, 0, message_format, {start_inclusive}.value, {end_exclusive}.value); + Text message = {{.value = malloc(length + 1)}}; + snprintf(message.value, length + 1, message_format, {start_inclusive}.value, {end_exclusive}.value); + builtinPanic$$Text(message); }} - size_t length = {end_exclusive}->value - {start_inclusive}->value;\n\ - char* result = malloc(length + 1);\n\ - memcpy(result, {text}->value + {start_inclusive}->value, length);\n\ - Text* result_pointer = malloc(sizeof(Text)); - result_pointer->value = result; - return result_pointer;", + size_t length = {end_exclusive}.value - {start_inclusive}.value;\n\ + Text result = {{.value = malloc(length + 1)}};\n\ + memcpy(result.value, {text}.value + {start_inclusive}.value, length);\n\ + return result;", text = function.parameters[0].id, start_inclusive = function.parameters[1].id, end_exclusive = function.parameters[2].id, )), BuiltinFunction::TextIndexOf => self.push(format!( "\ - {return_type}* result_pointer = malloc(sizeof({return_type})); - char* result = strstr({a}->value, {b}->value); - if (result == NULL) {{ - result_pointer->variant = {return_type}_none; + {return_type} result; + char* result_raw = strstr({a}.value, {b}.value); + if (result_raw == NULL) {{ + result = {{.variant = {return_type}_none}}; }} else {{ - result_pointer->variant = {return_type}_some; - Int* index_pointer = malloc(sizeof(Int)); - index_pointer->value = result - {a}->value; - result_pointer->value.some = index_pointer; + result = {{ + .variant = {return_type}_some, + .value.some = {{.value = result_raw - {a}.value}}, + }}; }} - return result_pointer;", + return result;", a = function.parameters[0].id, b = function.parameters[1].id, return_type = function.return_type, )), BuiltinFunction::TextLength => self.push(format!( "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = strlen({text}->value); - return result_pointer;", + Int result = {{.value = strlen({text}.value)}}; + return result;", text = function.parameters[0].id, )), } @@ -748,18 +741,14 @@ impl<'h> Context<'h> { fn lower_expression(&mut self, declaration_name: &str, id: Id, expression: &Expression) { match &expression.kind { ExpressionKind::Int(int) => { - self.push(format!( - "{}* {id} = malloc(sizeof({}));", - &expression.type_, &expression.type_, - )); - self.push(format!("{id}->value = {int};")); + self.push(format!("{} {id} = {{.value = {int} }};", &expression.type_)); } ExpressionKind::Text(text) => { self.push(format!( - "{}* {id} = malloc(sizeof({}));", - &expression.type_, &expression.type_, + "{} {id} = {{.value = \"{}\"}};", + &expression.type_, + text.escape_default(), )); - self.push(format!("{id}->value = \"{}\";", text.escape_default())); } ExpressionKind::CreateStruct { struct_, fields } => { let TypeDeclaration::Struct { @@ -769,42 +758,66 @@ impl<'h> Context<'h> { unreachable!(); }; - self.push(format!( - "{}* {id} = malloc(sizeof({}));", - &expression.type_, &expression.type_, - )); + self.push(format!("{} {id} = {{", &expression.type_)); for ((name, _), value) in type_fields.iter().zip_eq(fields.iter()) { - self.push(format!("\n{id}->{name} = {value};")); + self.push(format!("\n.{name} = {value};")); } + self.push("};"); } ExpressionKind::StructAccess { struct_, field } => { - self.push(format!("{}* {id} = {struct_}->{field};", &expression.type_)); + self.push(format!("{} {id} = {struct_}.{field};", &expression.type_)); } ExpressionKind::CreateEnum { enum_, variant, value, } => { - self.push(format!( - "{}* {id} = malloc(sizeof({}));", - &expression.type_, &expression.type_, - )); - self.push(format!("{id}->variant = {enum_}_{variant};")); - if let Some(value) = value { - self.push(format!("\n{id}->value.{variant} = {value};")); + if self.get_boxed_variants(enum_).contains(variant) { + let TypeDeclaration::Enum { variants } = &self.mono.type_declarations[enum_] + else { + unreachable!() + }; + let value_type = variants + .iter() + .find(|it| &it.name == variant) + .as_ref() + .unwrap() + .value_type + .as_ref() + .unwrap(); + self.push(format!( + "\ + {value_type}* {id}_value_boxed = malloc(sizeof({value_type}*)); + *{id}_value_boxed = {value}; + {type_} {id} = {{ + .variant = {enum_}_{variant}, + .value.{variant} = {id}_value_boxed, + }};", + value = value.unwrap(), + type_ = &expression.type_, + )); + } else { + self.push(format!( + "{} {id} = {{.variant = {enum_}_{variant}", + &expression.type_, + )); + if let Some(value) = value { + self.push(format!("\n.value.{variant} = {value};")); + } + self.push("};"); } } ExpressionKind::GlobalAssignmentReference(assignment) => { - self.push(format!("{}* {id} = {assignment};", &expression.type_)); + self.push(format!("{} {id} = {assignment};", &expression.type_)); } ExpressionKind::LocalReference(referenced_id) => { - self.push(format!("{}* {id} = {referenced_id};", &expression.type_)); + self.push(format!("{} {id} = {referenced_id};", &expression.type_)); } ExpressionKind::CallFunction { function, arguments, } => { - self.push(format!("{}* {id} = {function}(", &expression.type_)); + self.push(format!("{} {id} = {function}(", &expression.type_)); for (index, argument) in arguments.iter().enumerate() { if index > 0 { self.push(", "); @@ -815,7 +828,7 @@ impl<'h> Context<'h> { } ExpressionKind::CallLambda { lambda, arguments } => { self.push(format!( - "{}* {id} = {lambda}->function({lambda}->closure", + "{} {id} = {lambda}.function({lambda}.closure", &expression.type_ )); for argument in &**arguments { @@ -828,14 +841,19 @@ impl<'h> Context<'h> { enum_, cases, } => { - self.push(format!("{}* {id};\n", &expression.type_)); + self.push(format!("{} {id};\n", &expression.type_)); - self.push(format!("switch ({value}->variant) {{")); + self.push(format!("switch ({value}.variant) {{")); for case in &**cases { self.push(format!("case {enum_}_{}:\n", case.variant)); if let Some((value_id, value_type)) = &case.value { self.push(format!( - "{value_type}* {value_id} = {value}->value.{};\n", + "{value_type} {value_id} = {}{value}.value.{};\n", + if self.get_boxed_variants(enum_).contains(&case.variant) { + "*" + } else { + "" + }, case.variant, )); } @@ -859,13 +877,13 @@ impl<'h> Context<'h> { )); } self.push(format!( - "{type_}* {id} = malloc(sizeof({type_}));", + "\ + {type_} {id} = {{ + .closure = {id}_closure, + .function = {declaration_name}$lambda{id}_function, + }};", type_ = &expression.type_, )); - self.push(format!("{id}->closure = {id}_closure;")); - self.push(format!( - "{id}->function = {declaration_name}$lambda{id}_function;", - )); } } } From 557b46ca8dd5eec1e3a47157f40ccec87d5358df Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 20:02:03 +0100 Subject: [PATCH 12/17] Fix typo --- compiler_v4/src/mono_to_c.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index 7506b3663..417d89adb 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -760,7 +760,7 @@ impl<'h> Context<'h> { self.push(format!("{} {id} = {{", &expression.type_)); for ((name, _), value) in type_fields.iter().zip_eq(fields.iter()) { - self.push(format!("\n.{name} = {value};")); + self.push(format!("\n.{name} = {value},")); } self.push("};"); } From 9ee2a4fb3b6a8f89fa99e6849d0370130ec0cac9 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 20:02:31 +0100 Subject: [PATCH 13/17] Fix memory layouting for recursive enums --- compiler_v4/src/memory_layout.rs | 42 ++++++++++---------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/compiler_v4/src/memory_layout.rs b/compiler_v4/src/memory_layout.rs index d40103ded..6ae753bd7 100644 --- a/compiler_v4/src/memory_layout.rs +++ b/compiler_v4/src/memory_layout.rs @@ -33,7 +33,6 @@ impl Layout { #[must_use] pub const fn new(size: usize, alignment: Alignment) -> Self { - assert!(size.is_multiple_of(alignment.get())); Self { size, alignment } } } @@ -88,7 +87,6 @@ impl<'m> Context<'m> { } } - /// `None` means a recursive struct type that would have infinite size. fn lay_out(&mut self, type_: &str) -> Option { match self.memory_layouts.entry(type_.into()) { Entry::Occupied(entry) => { @@ -113,10 +111,14 @@ impl<'m> Context<'m> { } } TypeDeclaration::Struct { fields } => { - let field_layouts = fields + let Some(field_layouts) = fields .iter() .map(|(name, type_)| try { (name, self.lay_out(type_)?) }) - .collect::>>()?; + .collect::>>() + else { + assert!(self.memory_layouts.remove(type_).unwrap().is_none()); + return None; + }; let parts = field_layouts .iter() .map(|(_, layout)| *layout) @@ -143,11 +145,10 @@ impl<'m> Context<'m> { let mut boxed_variants = FxHashSet::default(); for variant in variants { if let Some(value_type) = variant.value_type.as_ref() { - let mut layout = self.lay_out(value_type)?; - if self.is_field_recursive(type_, value_type) { - layout = Layout::POINTER; + let layout = self.lay_out(value_type).unwrap_or_else(|| { boxed_variants.force_insert(variant.name.clone()); - } + Layout::POINTER + }); size = size.max(layout.size); alignment = alignment.max(layout.alignment); } @@ -168,30 +169,13 @@ impl<'m> Context<'m> { }, }; let layout = type_layout.layout; - self.memory_layouts + assert!(self + .memory_layouts .insert(type_.into(), Some(type_layout)) - .unwrap(); + .unwrap() + .is_none()); Some(layout) } - fn is_field_recursive(&self, outer_type: &str, field_type: &str) -> bool { - if outer_type == field_type { - return true; - } - - match &self.type_declarations[field_type] { - TypeDeclaration::Builtin(_) => false, - TypeDeclaration::Struct { fields } => fields - .iter() - .any(|(_, field_type)| self.is_field_recursive(outer_type, field_type)), - TypeDeclaration::Enum { variants } => variants.iter().any(|variant| { - variant - .value_type - .iter() - .any(|field_type| self.is_field_recursive(outer_type, field_type)) - }), - TypeDeclaration::Function { .. } => false, - } - } fn lay_out_aggregate(parts: &[Layout]) -> AggregateLayout { if parts.is_empty() { From 98bc6cb925bb4cf41d47fa5ba295202e0708c594 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Mon, 3 Feb 2025 20:02:42 +0100 Subject: [PATCH 14/17] Fix type declaration order in C --- compiler_v4/src/hir_to_mono.rs | 3 ++- compiler_v4/src/memory_layout.rs | 13 ++++++++++--- compiler_v4/src/mono.rs | 1 + compiler_v4/src/mono_to_c.rs | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index c4cccb0d2..385f9294f 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -40,7 +40,7 @@ impl<'h> Context<'h> { .into_iter() .map(|(name, declaration)| (name, declaration.unwrap())) .collect(); - let memory_layouts = lay_out_memory(&type_declarations); + let (memory_layouts, type_declaration_order) = lay_out_memory(&type_declarations); Mono { type_declarations, @@ -58,6 +58,7 @@ impl<'h> Context<'h> { .map(|(name, function)| (name, function.unwrap())) .collect(), memory_layouts, + type_declaration_order, main_function, } } diff --git a/compiler_v4/src/memory_layout.rs b/compiler_v4/src/memory_layout.rs index 6ae753bd7..2de2112d3 100644 --- a/compiler_v4/src/memory_layout.rs +++ b/compiler_v4/src/memory_layout.rs @@ -64,26 +64,32 @@ pub struct AggregateLayout { pub fn lay_out_memory( type_declarations: &FxHashMap, TypeDeclaration>, -) -> FxHashMap, TypeLayout> { +) -> (FxHashMap, TypeLayout>, Box<[Box]>) { let mut context = Context::new(type_declarations); for type_ in type_declarations.keys() { context.lay_out(type_); } - context + let memory_layouts = context .memory_layouts .into_iter() .map(|(type_, layout)| (type_, layout.unwrap())) - .collect() + .collect(); + ( + memory_layouts, + context.sorted_declarations.into_boxed_slice(), + ) } struct Context<'m> { type_declarations: &'m FxHashMap, TypeDeclaration>, memory_layouts: FxHashMap, Option>, + sorted_declarations: Vec>, } impl<'m> Context<'m> { fn new(type_declarations: &'m FxHashMap, TypeDeclaration>) -> Self { Self { type_declarations, memory_layouts: FxHashMap::default(), + sorted_declarations: Vec::new(), } } @@ -168,6 +174,7 @@ impl<'m> Context<'m> { kind: TypeLayoutKind::Builtin, }, }; + self.sorted_declarations.push(type_.into()); let layout = type_layout.layout; assert!(self .memory_layouts diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index ac615e65d..5b2955284 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -27,6 +27,7 @@ impl ToText for Id { pub struct Mono { pub type_declarations: FxHashMap, TypeDeclaration>, pub memory_layouts: FxHashMap, TypeLayout>, + pub type_declaration_order: Box<[Box]>, pub assignments: FxHashMap, Assignment>, pub assignment_initialization_order: Box<[Box]>, pub functions: FxHashMap, Function>, diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index 417d89adb..79c634dfb 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -72,8 +72,8 @@ impl<'h> Context<'h> { } } fn lower_type_declarations(&mut self) { - // FIXME: topological sort - for (name, declaration) in &self.mono.type_declarations { + for name in &self.mono.type_declaration_order { + let declaration = &self.mono.type_declarations[name]; self.push(format!("struct {name} {{\n")); match declaration { TypeDeclaration::Builtin(builtin_type) => { From 16cea2c244d3f2ac3f50a74f950a6fc5ea7ccbaa Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 2 Oct 2025 13:39:08 +0200 Subject: [PATCH 15/17] Simplify comment --- vscode_extension_v4/src/extension.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vscode_extension_v4/src/extension.ts b/vscode_extension_v4/src/extension.ts index 8052aed1d..04f53143f 100644 --- a/vscode_extension_v4/src/extension.ts +++ b/vscode_extension_v4/src/extension.ts @@ -17,9 +17,8 @@ export function activate(context: vscode.ExtensionContext) { } // Updates can be triggered very frequently (on every keystroke), but they can -// take long – for example, when editing the Candy compiler itself, simply -// analyzing the files takes some time. Thus, here we make sure that only one -// update runs at a time. +// take long to process. Thus, here we make sure that only one update runs at a +// time. let generation = 0; let currentRun = Promise.resolve(null); async function onlyRunOneAtATime(callback: () => Promise) { From eed486f942dafd826ce1b9b18e3580eed1306edc Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 2 Oct 2025 13:44:15 +0200 Subject: [PATCH 16/17] Fix Clippy complaint --- compiler_v4/src/hir_to_mono.rs | 6 +++--- compiler_v4/src/memory_layout.rs | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 385f9294f..60f581383 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -40,7 +40,7 @@ impl<'h> Context<'h> { .into_iter() .map(|(name, declaration)| (name, declaration.unwrap())) .collect(); - let (memory_layouts, type_declaration_order) = lay_out_memory(&type_declarations); + let memory_layout_result = lay_out_memory(&type_declarations); Mono { type_declarations, @@ -57,8 +57,8 @@ impl<'h> Context<'h> { .into_iter() .map(|(name, function)| (name, function.unwrap())) .collect(), - memory_layouts, - type_declaration_order, + memory_layouts: memory_layout_result.memory_layouts, + type_declaration_order: memory_layout_result.type_declaration_order, main_function, } } diff --git a/compiler_v4/src/memory_layout.rs b/compiler_v4/src/memory_layout.rs index 2de2112d3..391c25355 100644 --- a/compiler_v4/src/memory_layout.rs +++ b/compiler_v4/src/memory_layout.rs @@ -62,9 +62,12 @@ pub struct AggregateLayout { part_offsets: Box<[usize]>, } -pub fn lay_out_memory( - type_declarations: &FxHashMap, TypeDeclaration>, -) -> (FxHashMap, TypeLayout>, Box<[Box]>) { +pub struct LayoutResult { + pub memory_layouts: FxHashMap, TypeLayout>, + pub type_declaration_order: Box<[Box]>, +} + +pub fn lay_out_memory(type_declarations: &FxHashMap, TypeDeclaration>) -> LayoutResult { let mut context = Context::new(type_declarations); for type_ in type_declarations.keys() { context.lay_out(type_); @@ -74,10 +77,10 @@ pub fn lay_out_memory( .into_iter() .map(|(type_, layout)| (type_, layout.unwrap())) .collect(); - ( + LayoutResult { memory_layouts, - context.sorted_declarations.into_boxed_slice(), - ) + type_declaration_order: context.sorted_declarations.into_boxed_slice(), + } } struct Context<'m> { type_declarations: &'m FxHashMap, TypeDeclaration>, From 36f71d6018512fdde3861d0e747bf89c245d64f5 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 2 Oct 2025 13:49:49 +0200 Subject: [PATCH 17/17] Limit CI to new compiler --- .github/workflows/compiler.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler.yaml b/.github/workflows/compiler.yaml index 2dbd38059..9c7922ad3 100644 --- a/.github/workflows/compiler.yaml +++ b/.github/workflows/compiler.yaml @@ -18,11 +18,11 @@ jobs: - uses: Swatinem/rust-cache@v2.7.5 - name: "Compiler: clippy" - run: cargo clippy -- --deny warnings + run: cargo clippy --package candy_compiler_v4 -- --deny warnings - name: "Compiler: test" - run: cargo test --workspace + run: cargo test --package candy_compiler_v4 - name: "Compiler: fmt" - run: cargo fmt --check + run: cargo fmt --package candy_compiler_v4 --check vscode-extension-check: name: Check VS Code Extension