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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ members = [
]
resolver = "3"

[workspace.lints.rust]
missing_docs = "warn"

[workspace.package]
authors = ["Takahiro Sato <harehare1110@gmail.com>"]
edition = "2024"
Expand Down
3 changes: 3 additions & 0 deletions crates/mq-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ readme = "README.md"
repository.workspace = true
version.workspace = true

[lints]
workspace = true

[dependencies]
clap = {workspace = true, features = ["derive"]}
glob = {workspace = true}
Expand Down
2 changes: 2 additions & 0 deletions crates/mq-bench/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Runner for `bench_` functions in `.mq` files.

mod runner;

pub use runner::{BenchRecord, BenchRunner, OutputFormat};
2 changes: 2 additions & 0 deletions crates/mq-bench/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Command-line runner for `bench_` functions in `.mq` files.

mod runner;

use clap::Parser;
Expand Down
7 changes: 7 additions & 0 deletions crates/mq-bench/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ struct DiscoveredBench {
/// Timing summary for a single bench function.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchRecord {
/// Source file of the benchmark.
pub file: String,
/// Benchmark name.
pub name: String,
/// Number of timed iterations.
pub iterations: usize,
/// Mean time per iteration in nanoseconds.
pub mean_ns: u128,
/// Median time per iteration in nanoseconds.
pub median_ns: u128,
/// Fastest iteration in nanoseconds.
pub min_ns: u128,
/// Slowest iteration in nanoseconds.
pub max_ns: u128,
}

Expand Down
3 changes: 3 additions & 0 deletions crates/mq-check/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pkg-fmt = "bin"
pkg-url = "{ repo }/releases/download/v{ version }/{ bin }-{ target }{ binary-ext }"
disabled-strategies = ["quick-install"]

[lints]
workspace = true

[dependencies]
clap = {workspace = true, features = ["derive"], optional = true}
colored = {workspace = true, optional = true}
Expand Down
3 changes: 3 additions & 0 deletions crates/mq-check/benches/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//! Type checker benchmarks.

//! Benchmark inputs shared by `benchmark.rs` and `examples/phase_profile.rs`.

/// Named inputs: a tiny program, real-world code of builtin.mq size, and synthetic code with
/// `N` functions sharing parameter names (the quadratic case in name resolution and calls).
pub const INPUTS: &[&str] = &["small", "builtin", "synthetic_200", "synthetic_1000", "synthetic_4000"];

/// Returns the source text for the named benchmark input.
pub fn input(name: &str) -> String {
match name {
"small" => "def f(x): x + 1; | f(2)".to_string(),
Expand Down
8 changes: 8 additions & 0 deletions crates/mq-check/src/kind_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,32 @@ impl KindSet {
.fold(Self::EMPTY, |set, kind| set.union(Self::of(kind)))
}

/// Whether `kind` is in the set.
pub const fn contains(self, kind: NodeKind) -> bool {
self.0 & Self::bit(kind) != 0
}

/// Whether the set is empty.
pub const fn is_empty(self) -> bool {
self.0 == 0
}

/// Whether every kind is in the set.
pub const fn is_all(self) -> bool {
self.0 == Self::ALL.0
}

/// Number of kinds in the set.
pub const fn len(self) -> usize {
self.0.count_ones() as usize
}

/// The kinds in either set.
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}

/// The kinds in both sets.
pub const fn intersect(self, other: Self) -> Self {
Self(self.0 & other.0)
}
Expand All @@ -66,10 +72,12 @@ impl KindSet {
Self(self.0 & !other.0)
}

/// Whether every kind of `self` is in `other`.
pub const fn is_subset_of(self, other: Self) -> bool {
self.0 & !other.0 == 0
}

/// Whether the sets share any kind.
pub const fn intersects(self, other: Self) -> bool {
self.0 & other.0 != 0
}
Expand Down
49 changes: 49 additions & 0 deletions crates/mq-check/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,27 @@ impl TypeEnv {
Self(FxHashMap::with_capacity_and_hasher(capacity, Default::default()))
}

/// Associates `scheme` with `symbol_id`, replacing any previous entry.
pub fn insert(&mut self, symbol_id: SymbolId, scheme: TypeScheme) {
self.0.insert(symbol_id, scheme);
}

/// The scheme for `symbol_id`, if any.
pub fn get(&self, symbol_id: &SymbolId) -> Option<&TypeScheme> {
self.0.get(symbol_id)
}

/// All symbol-to-scheme entries.
pub fn get_all(&self) -> &FxHashMap<SymbolId, TypeScheme> {
&self.0
}

/// Number of entries.
pub fn len(&self) -> usize {
self.0.len()
}

/// Whether the environment has no entries.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
Expand All @@ -89,87 +94,126 @@ pub enum TypeError {
#[error("Type mismatch: expected {expected}, found {found}")]
#[diagnostic(code(typechecker::type_mismatch))]
#[allow(dead_code)]
/// Expected and found types differ.
Mismatch {
/// The expected type.
expected: String,
/// The type that was found.
found: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
#[help]
/// Extra hint shown as help.
context: Option<String>,
},
#[error("Cannot unify types: {left} and {right}")]
#[diagnostic(code(typechecker::unification_error))]
#[allow(dead_code)]
/// Two types cannot be unified.
UnificationError {
/// Left-hand type.
left: String,
/// Right-hand type.
right: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
#[help]
/// Extra hint shown as help.
context: Option<String>,
},
#[error("Occurs check failed: type variable {var} occurs in {ty}")]
#[diagnostic(code(typechecker::occurs_check))]
#[allow(dead_code)]
/// A type variable occurs inside the type it is bound to.
OccursCheck {
/// The type variable.
var: String,
/// The type containing the variable.
ty: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
#[error("Undefined symbol: {name}")]
#[diagnostic(code(typechecker::undefined_symbol))]
#[allow(dead_code)]
/// A symbol is used but never defined.
UndefinedSymbol {
/// Name of the undefined symbol.
name: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
#[error("Wrong number of arguments: expected {expected}, found {found}")]
#[diagnostic(code(typechecker::wrong_arity))]
#[allow(dead_code)]
/// A call passes the wrong number of arguments.
WrongArity {
/// The expected number of arguments.
expected: usize,
/// The number of arguments passed.
found: usize,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
#[help]
/// Extra hint shown as help.
context: Option<String>,
},
#[error("Undefined field `{field}` in record type {record_ty}")]
#[diagnostic(code(typechecker::undefined_field))]
#[allow(dead_code)]
/// A record type has no such field.
UndefinedField {
/// Name of the missing field.
field: String,
/// The record type that lacks the field.
record_ty: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
#[error("Attribute `{attr}` does not exist on {node_ty}")]
#[diagnostic(code(typechecker::undefined_attribute))]
#[allow(dead_code)]
/// A node type has no such attribute.
UndefinedAttribute {
/// Name of the missing attribute.
attr: String,
/// The node type that lacks the attribute.
node_ty: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
#[error("Heterogeneous array: elements have mixed types [{types}]")]
#[diagnostic(code(typechecker::heterogeneous_array))]
#[allow(dead_code)]
/// Array elements have mixed types.
HeterogeneousArray {
/// Comma-separated element types.
types: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
#[error("Type variable not found: {0}")]
#[diagnostic(code(typechecker::type_var_not_found))]
/// A type variable id has no binding.
TypeVarNotFound(String),
#[error("Internal error: {0}")]
#[diagnostic(code(typechecker::internal_error))]
/// An unexpected internal failure.
Internal(String),
/// Emitted when an operation receives a nullable argument and may silently
/// propagate `none` instead of producing the expected concrete type.
#[error("Operation `{op}` may propagate `none`: argument `{nullable_arg}` may be `none`")]
#[diagnostic(code(typechecker::nullable_propagation))]
#[allow(dead_code)]
NullablePropagation {
/// The operation being checked.
op: String,
/// The argument that may be `none`.
nullable_arg: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
#[help]
/// Extra hint shown as help.
context: Option<String>,
},
/// Emitted when a branch is statically determined to be unreachable based on
Expand All @@ -178,17 +222,22 @@ pub enum TypeError {
#[diagnostic(code(typechecker::unreachable_code))]
#[allow(dead_code)]
UnreachableCode {
/// Why the branch is unreachable.
reason: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
},
/// Emitted when a match expression does not cover all possible values of the matched type.
#[error("Non-exhaustive patterns: missing case for {missing}")]
#[diagnostic(code(typechecker::non_exhaustive_patterns))]
#[allow(dead_code)]
NonExhaustiveMatch {
/// Description of the uncovered case.
missing: String,
/// Source range of the error, if known.
location: Option<mq_lang::Range>,
#[help]
/// Extra hint shown as help.
context: Option<String>,
},
}
Expand Down
2 changes: 2 additions & 0 deletions crates/mq-check/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Command-line type checker for mq programs.

mod format;

use std::io::{self, BufWriter, Read, Write};
Expand Down
3 changes: 3 additions & 0 deletions crates/mq-crawler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ readme = "README.md"
repository.workspace = true
version.workspace = true

[lints]
workspace = true

[dependencies]
base64 = {workspace = true}
chromiumoxide = {workspace = true}
Expand Down
Loading
Loading