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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ jobs:
path: Cargo.lock

msrv:
name: MSRV (1.68) Compiles
name: MSRV (1.71) Compiles
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.68
toolchain: 1.71
- run: cargo check

style-check:
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ categories = ["development-tools", "compilers"]
# MSRV 1.61.0 is the old MSRV of syn
# MRSV 1.63.0 is needed to support RefMut::filter_map
# MRSV 1.68.0 is the new latest MSRV of syn (as of syn 2.0.107 released on 19th October 2025)
# MRSV 1.71.0 is the new latest MSRV of unicode-ident (released on 8th February 2026)
# If changing this, also update the local-check-msrv.sh script and ci.yml
rust-version = "1.68"
rust-version = "1.71"

[lib]
proc-macro = true
Expand Down
4 changes: 2 additions & 2 deletions local-check-msrv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ set -e

cd "$(dirname "$0")"

rustup install 1.68
rm Cargo.lock && rustup run 1.68 cargo check
rustup install 1.71
rm Cargo.lock && rustup run 1.71 cargo check
34 changes: 17 additions & 17 deletions plans/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,26 +237,25 @@ Moved to [2026-01-types-and-forms.md](./2026-01-types-and-forms.md).
- [x] Allow shared / mutable arguments
- [x] Add tests for shared/mutable, including conversions working and not
- [ ] Iterable methods:
- [ ] Unpick `feat/iterable-map` and work our what we want to keep:
* Half-baked `FunctionValue` changes to allow invocation
* Separation of `ExecutionInterrupt` and `FunctionError` - do we actually want / need it?
- Perhaps `Result` -> `core::Result`
- `ExecutionResult` -> `ExpressionResult`
- And `FunctionResult` -> `Result` ?
* `Iterator` returns `Result` change -> do we want it?
* `Iterator` comparison can return errors -> maybe we just have `iterator != iterator` unless `std::ptr::eq`?
- [x] Unpick `feat/iterable-map` and work our what we want to keep:
- [x] Separation of `ExecutionInterrupt` and `FunctionError` - incorporated below
- [x] `Iterator` returns `Result` change -> replaced with below
* To implement `.map()`, we have a few things we need first:
- An iterator trait where Interpreter is passed at next time.
- Possibly - not require `Clone` on iterators:
-[x] An iterator trait where Interpreter is passed at next time.
-[ ] Possibly - not require `Clone` on iterators:
- Make `TryClone -> Result<T, &T>`
- Make `to_string` for iterator return `Iterator[?]`
- [ ] Create new iterator trait `PreinterpretIterator` and `IntoPreinterpretIterator` with `.next(&mut Interpreter)`
- [ ] Blanket implement it for iterator
- [ ] Then replace e.g. for loop with it.
- [ ] Create `Map` and `Filter` types on top of it, to be able to
implement `map` and `filter`
- [x] Create new iterator trait `PreinterpretIterator` and `IntoPreinterpretIterator` with `.next(&mut Interpreter)`
- [x] Blanket implement it for `Iterator<AnyValue> + Clone`
- [x] Then replace e.g. for loop impl with it.
- [x] Create `Map` and `Filter` types on top of it, to be able to implement `map` and `filter`
- [ ] See if new iterators on iterator value can be fixed to be lazy
- [ ] Salvage half-baked `FunctionValue` changes to allow invocation
- [ ] Consider if IteratorValue should have `Item = ReturnedValue`
- [ ] Add `iterable.map`, `iterable.filter`, `iterable.flatten`, `iterable.flatmap`
- [ ] Add tests for iterable methods
- [ ] Add `array.sort`, `array.sort_by`
- [ ] Look into if a closure like `|| { }()` can evade `attempt` block statue mutation checks. Maybe lean into it as a way to avoid htem, and mention it in the error message
- [ ] Resolve all `TODO[functions]`

Possible punted:
Expand Down Expand Up @@ -458,8 +457,9 @@ preinterpret::run! {

- [ ] Look at benchmarks and if anything should be sped up
- [ ] Speeding up stream literal processing
- [ ] When interpreting a stream literal, we can avoid having to go through error handling pathways to get an `output` from the intepreter by storing a `OutputInterpreter<'a>` which wraps an `&mut OutputStream` and a pointer to an Intepreter, and can be converted back into/from an `Interpreter` easily
- [ ] Possibly similarly for an `InputInterpreter<'a>` when processing a `ConsumeStream`
- [ ] Avoid a little overhead by having `OutputInterpreter<'a>` store a pointer to interpreter and output stream and a `PhantomData<&'a Interpreter>` / `PhantomData<&'a OutputStream>`, and recreate output stream after a call to with_interpreter.
... and get rid of `output_stack_height`, `current_output_unchecked`, `current_output_mut_unchecked`
- [ ] Create an `InputInterpreter<'a>` when processing a `ConsumeStream`
- [ ] Speeding up scopes at runtime:
- [ ] In the interpreter, store a flattened stack of variable values
- [ ] `no_mutation_above` can be a stack offset
Expand Down
32 changes: 20 additions & 12 deletions src/expressions/closures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl ClosureExpression {
&self,
interpreter: &mut Interpreter,
ownership: RequestedOwnership,
) -> ExecutionResult<Spanned<RequestedValue>> {
) -> FunctionResult<Spanned<RequestedValue>> {
let span_range = self.0.span_range;
let value = ClosureValue {
definition: Rc::clone(&self.0),
Expand Down Expand Up @@ -61,14 +61,17 @@ impl ClosureValue {
self,
arguments: Vec<Spanned<ArgumentValue>>,
context: &mut FunctionCallContext,
) -> ExecutionResult<Spanned<ReturnedValue>> {
) -> FunctionResult<Spanned<ReturnedValue>> {
let definition = &*self.definition;

context.interpreter.enter_function_boundary_scope(
definition.scope_id,
definition.frame_id,
context.output_span_range,
)?;
context
.interpreter
.enter_function_boundary_scope(
definition.scope_id,
definition.frame_id,
context.output_span_range,
)
.expect_no_interrupts()?;
for (definition, content) in self.closed_references {
context.interpreter.define_variable(definition, content);
}
Expand All @@ -78,7 +81,9 @@ impl ClosureValue {
{
match (pattern, arg) {
(pattern, ArgumentValue::Owned(owned)) => {
pattern.handle_destructure(context.interpreter, owned)?;
pattern
.handle_destructure(context.interpreter, owned)
.expect_no_interrupts()?;
}
(Pattern::Discarded(_), _) => {}
(Pattern::Variable(variable), ArgumentValue::Shared(shared)) => {
Expand Down Expand Up @@ -111,10 +116,13 @@ impl ClosureValue {
}
}

let Spanned(output, body_span) = definition.body.evaluate(
context.interpreter,
RequestedOwnership::Concrete(ArgumentOwnership::AsIs),
)?;
let Spanned(output, body_span) = definition
.body
.evaluate(
context.interpreter,
RequestedOwnership::Concrete(ArgumentOwnership::AsIs),
)
.expect_no_interrupts()?;

let returned_value = match output {
RequestedValue::Owned(any_value) => ReturnedValue::Owned(any_value),
Expand Down
10 changes: 5 additions & 5 deletions src/expressions/concepts/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ where
pub(crate) fn downcast_resolve<X: FromSpannedValueContent<'a, Form = C::Form>>(
self,
resolution_target: &str,
) -> ExecutionResult<X>
) -> FunctionResult<X>
where
<X as IsValueContent>::Type: DowncastFrom<C::Type>,
{
Expand All @@ -107,11 +107,11 @@ where
}

// TODO[concepts]: Change to use a FromSpannedDynContent trait,
// so that it can return a ExecutionResult<X> and avoid needing to specify D.
// so that it can return a FunctionResult<X> and avoid needing to specify D.
pub(crate) fn dyn_resolve<D: IsDynLeaf + ?Sized>(
self,
resolution_target: &str,
) -> ExecutionResult<DynContent<'a, D::Type, C::Form>>
) -> FunctionResult<DynContent<'a, D::Type, C::Form>>
where
<D as IsDynLeaf>::Type: DynResolveFrom<C::Type>,
C::Form: IsDynCompatibleForm,
Expand Down Expand Up @@ -225,7 +225,7 @@ impl<
{
type ValueType = T;
const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP;
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> ExecutionResult<Self> {
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> FunctionResult<Self> {
let ownership_mapped = F::from_argument_value(value)?;
let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?;
Ok(X::from_content(type_mapped))
Expand All @@ -240,7 +240,7 @@ impl<
T: UpcastTo<AnyType>,
> IsReturnable for X
{
fn to_returned_value(self) -> ExecutionResult<ReturnedValue> {
fn to_returned_value(self) -> FunctionResult<ReturnedValue> {
let type_mapped = self.into_content().upcast::<AnyType>();
BeOwned::into_returned_value(type_mapped)
}
Expand Down
10 changes: 4 additions & 6 deletions src/expressions/concepts/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,11 @@ pub(crate) trait IsDynCompatibleForm: IsHierarchicalForm {
pub(crate) trait MapFromArgument: IsHierarchicalForm {
const ARGUMENT_OWNERSHIP: ArgumentOwnership;

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>>;
fn from_argument_value(value: ArgumentValue)
-> FunctionResult<Content<'static, AnyType, Self>>;
}

pub(crate) trait MapIntoReturned: IsHierarchicalForm {
fn into_returned_value(
value: Content<'static, AnyType, Self>,
) -> ExecutionResult<ReturnedValue>;
fn into_returned_value(value: Content<'static, AnyType, Self>)
-> FunctionResult<ReturnedValue>;
}
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/any_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl MapFromArgument for BeAnyMut {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value
.expect_mutable()
.0
Expand Down
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/any_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl MapFromArgument for BeAnyRef {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value
.expect_shared()
.0
Expand Down
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl MapFromArgument for BeArgument {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
todo!("Argument")
}
}
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/assignee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl MapFromArgument for BeAssignee {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value.expect_assignee().into_content())
}
}
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/copy_on_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl MapFromArgument for BeCopyOnWrite {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
match value.expect_copy_on_write().inner {
CopyOnWriteInner::Owned(owned) => Ok(BeCopyOnWrite::new_owned(owned)),
CopyOnWriteInner::SharedWithInfallibleCloning(shared) => {
Expand Down
2 changes: 1 addition & 1 deletion src/expressions/concepts/forms/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl MapFromArgument for BeMutable {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value.expect_mutable().into_content())
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/expressions/concepts/forms/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ impl MapFromArgument for BeOwned {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value.expect_owned())
}
}

impl MapIntoReturned for BeOwned {
fn into_returned_value(
content: Content<'static, AnyType, Self>,
) -> ExecutionResult<ReturnedValue> {
) -> FunctionResult<ReturnedValue> {
Ok(ReturnedValue::Owned(content))
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/expressions/concepts/forms/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ impl MapFromArgument for BeShared {

fn from_argument_value(
value: ArgumentValue,
) -> ExecutionResult<Content<'static, AnyType, Self>> {
) -> FunctionResult<Content<'static, AnyType, Self>> {
Ok(value.expect_shared().into_content())
}
}

// impl MapIntoReturned for BeShared {
// fn into_returned_value(
// content: Content<'static, AnyType, Self>,
// ) -> ExecutionResult<ReturnedValue> {
// ) -> FunctionResult<ReturnedValue> {
// todo!("Return shared")
// }
// }
10 changes: 5 additions & 5 deletions src/expressions/concepts/type_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub(crate) trait DowncastFrom<T: IsHierarchicalType>: IsHierarchicalType {
content: Content<'a, T, F>,
span_range: SpanRange,
resolution_target: &str,
) -> ExecutionResult<Content<'a, Self, F>> {
) -> FunctionResult<Content<'a, Self, F>> {
let content = match Self::downcast_from(content) {
Ok(c) => c,
Err(existing) => {
Expand All @@ -112,7 +112,7 @@ pub(crate) trait DynResolveFrom<T: IsHierarchicalType>: IsDynType {
content: Content<'a, T, F>,
span_range: SpanRange,
resolution_target: &str,
) -> ExecutionResult<DynContent<'a, Self, F>> {
) -> FunctionResult<DynContent<'a, Self, F>> {
let content = match Self::downcast_from(content) {
Ok(c) => c,
Err(existing) => {
Expand Down Expand Up @@ -816,7 +816,7 @@ macro_rules! define_dyn_type {
impl IsArgument for Box<$dyn_type> {
type ValueType = $type_def;
const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned;
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> ExecutionResult<Self> {
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> FunctionResult<Self> {
let form_mapped = BeOwned::from_argument_value(value)?;
<$type_def as DynResolveFrom<AnyType>>::resolve(form_mapped, span_range, "This argument")
}
Expand All @@ -825,7 +825,7 @@ macro_rules! define_dyn_type {
impl<'a> IsArgument for AnyRef<'a, $dyn_type> {
type ValueType = $type_def;
const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared;
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> ExecutionResult<Self> {
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> FunctionResult<Self> {
let form_mapped = BeAnyRef::from_argument_value(value)?;
<$type_def as DynResolveFrom<AnyType>>::resolve(form_mapped, span_range, "This argument")
}
Expand All @@ -834,7 +834,7 @@ macro_rules! define_dyn_type {
impl<'a> IsArgument for AnyMut<'a, $dyn_type> {
type ValueType = $type_def;
const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable;
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> ExecutionResult<Self> {
fn from_argument(Spanned(value, span_range): Spanned<ArgumentValue>) -> FunctionResult<Self> {
let form_mapped = BeAnyMut::from_argument_value(value)?;
<$type_def as DynResolveFrom<AnyType>>::resolve(form_mapped, span_range, "This argument")
}
Expand Down
Loading