Skip to content

Commit

Permalink
Introduce ControlFlow as the basis of the TRY operator
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Aug 23, 2023
1 parent fe8a183 commit e4b6798
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 122 deletions.
187 changes: 101 additions & 86 deletions crates/rune-macros/src/any.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::BTreeMap;

use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use rune_core::Hash;
use syn::punctuated::Punctuated;
Expand Down Expand Up @@ -51,8 +51,7 @@ impl InternalCall {
};

let name = syn::LitStr::new(&name.to_string(), name.span());

expand_any(&self.path, type_hash, &name, &[], &tokens, &generics)
expand_any(None, &self.path, type_hash, &name, &[], &tokens, &generics)
}
}

Expand Down Expand Up @@ -110,7 +109,15 @@ impl Derive {

let name = syn::LitStr::new(&name.to_string(), name.span());

expand_any(ident, type_hash, &name, &installers, &tokens, generics)
expand_any(
attr.builtin,
ident,
type_hash,
&name,
&installers,
&tokens,
generics,
)
}
}

Expand Down Expand Up @@ -334,7 +341,7 @@ fn expand_enum_install_with(
enum_.variant_mut(#variant_index)?.make_named(&[#(#field_names),*])?.static_docs(&#variant_docs)
});

variants.push((None, attr));
variants.push((None, variant_attr));
}
syn::Fields::Unnamed(fields) => {
let mut fields_len = 0usize;
Expand Down Expand Up @@ -373,7 +380,7 @@ fn expand_enum_install_with(
None
};

variants.push((constructor, attr));
variants.push((constructor, variant_attr));
}
syn::Fields::Unit => {
variant_metas.push(quote! {
Expand All @@ -386,7 +393,7 @@ fn expand_enum_install_with(
None
};

variants.push((constructor, attr));
variants.push((constructor, variant_attr));
}
}
}
Expand Down Expand Up @@ -463,6 +470,7 @@ fn expand_enum_install_with(

/// Expand the necessary implementation details for `Any`.
pub(super) fn expand_any<T>(
builtin: Option<Span>,
ident: T,
type_hash: Hash,
name: &syn::LitStr,
Expand Down Expand Up @@ -522,20 +530,6 @@ where
}
};

let type_hash = type_hash.into_inner();

let make_hash = if !generic_names.is_empty() {
quote!(#hash::new_with_type_parameters(#type_hash, #hash::parameters([#(<#generic_names as #type_of>::type_hash()),*])))
} else {
quote!(#hash::new(#type_hash))
};

let type_parameters = if !generic_names.is_empty() {
quote!(#hash::parameters([#(<#generic_names as #type_of>::type_hash()),*]))
} else {
quote!(#hash::EMPTY)
};

let install_with = quote! {
#[automatically_derived]
impl #impl_generics #install_with for #ident #type_generics #where_clause {
Expand All @@ -546,92 +540,113 @@ where
}
};

Ok(quote! {
#[automatically_derived]
impl #impl_generics #any for #ident #type_generics #where_clause {
fn type_hash() -> #hash {
#make_hash
}
}
let any = if builtin.is_none() {
let type_hash = type_hash.into_inner();

#install_with
#impl_named
let make_hash = if !generic_names.is_empty() {
quote!(#hash::new_with_type_parameters(#type_hash, #hash::parameters([#(<#generic_names as #type_of>::type_hash()),*])))
} else {
quote!(#hash::new(#type_hash))
};

#[automatically_derived]
impl #impl_generics #type_of for #ident #type_generics #where_clause {
#[inline]
fn type_hash() -> #hash {
<Self as #any>::type_hash()
}
let type_parameters = if !generic_names.is_empty() {
quote!(#hash::parameters([#(<#generic_names as #type_of>::type_hash()),*]))
} else {
quote!(#hash::EMPTY)
};

#[inline]
fn type_parameters() -> #hash {
#type_parameters
Some(quote! {
#[automatically_derived]
impl #impl_generics #any for #ident #type_generics #where_clause {
fn type_hash() -> #hash {
#make_hash
}
}

#[inline]
fn type_info() -> #type_info {
#type_info::Any(#any_type_info::__private_new(
#raw_str::from_str(core::any::type_name::<Self>()),
<Self as #type_of>::type_hash(),
))
#[automatically_derived]
impl #impl_generics #type_of for #ident #type_generics #where_clause {
#[inline]
fn type_hash() -> #hash {
<Self as #any>::type_hash()
}

#[inline]
fn type_parameters() -> #hash {
#type_parameters
}

#[inline]
fn type_info() -> #type_info {
#type_info::Any(#any_type_info::__private_new(
#raw_str::from_str(core::any::type_name::<Self>()),
<Self as #type_of>::type_hash(),
))
}
}
}

#[automatically_derived]
impl #impl_generics #maybe_type_of for #ident #type_generics #where_clause {
#[inline]
fn maybe_type_of() -> Option<#full_type_of> {
Some(<Self as #type_of>::type_of())
#[automatically_derived]
impl #impl_generics #maybe_type_of for #ident #type_generics #where_clause {
#[inline]
fn maybe_type_of() -> Option<#full_type_of> {
Some(<Self as #type_of>::type_of())
}
}
}

#[automatically_derived]
impl #impl_generics #unsafe_to_ref for #ident #type_generics #where_clause {
type Guard = #raw_into_ref;
#[automatically_derived]
impl #impl_generics #unsafe_to_ref for #ident #type_generics #where_clause {
type Guard = #raw_into_ref;

unsafe fn unsafe_to_ref<'a>(value: #value) -> #vm_result<(&'a Self, Self::Guard)> {
let (value, guard) = match value.into_any_ptr() {
#vm_result::Ok(value) => value,
#vm_result::Err(err) => return #vm_result::Err(err),
};
unsafe fn unsafe_to_ref<'a>(value: #value) -> #vm_result<(&'a Self, Self::Guard)> {
let (value, guard) = match value.into_any_ptr() {
#vm_result::Ok(value) => value,
#vm_result::Err(err) => return #vm_result::Err(err),
};

#vm_result::Ok((&*value, guard))
#vm_result::Ok((&*value, guard))
}
}
}

#[automatically_derived]
impl #impl_generics #unsafe_to_mut for #ident #type_generics #where_clause {
type Guard = #raw_into_mut;
#[automatically_derived]
impl #impl_generics #unsafe_to_mut for #ident #type_generics #where_clause {
type Guard = #raw_into_mut;

unsafe fn unsafe_to_mut<'a>(value: #value) -> #vm_result<(&'a mut Self, Self::Guard)> {
let (value, guard) = match value.into_any_mut() {
#vm_result::Ok(value) => value,
#vm_result::Err(err) => return #vm_result::Err(err),
};
unsafe fn unsafe_to_mut<'a>(value: #value) -> #vm_result<(&'a mut Self, Self::Guard)> {
let (value, guard) = match value.into_any_mut() {
#vm_result::Ok(value) => value,
#vm_result::Err(err) => return #vm_result::Err(err),
};

#vm_result::Ok((&mut *value, guard))
#vm_result::Ok((&mut *value, guard))
}
}
}

#[automatically_derived]
impl #impl_generics #unsafe_to_value for &#ident #type_generics #where_clause {
type Guard = #pointer_guard;
#[automatically_derived]
impl #impl_generics #unsafe_to_value for &#ident #type_generics #where_clause {
type Guard = #pointer_guard;

unsafe fn unsafe_to_value(self) -> #vm_result<(#value, Self::Guard)> {
let (shared, guard) = #shared::from_ref(self);
#vm_result::Ok((#value::from(shared), guard))
unsafe fn unsafe_to_value(self) -> #vm_result<(#value, Self::Guard)> {
let (shared, guard) = #shared::from_ref(self);
#vm_result::Ok((#value::from(shared), guard))
}
}
}

#[automatically_derived]
impl #impl_generics #unsafe_to_value for &mut #ident #type_generics #where_clause {
type Guard = #pointer_guard;
#[automatically_derived]
impl #impl_generics #unsafe_to_value for &mut #ident #type_generics #where_clause {
type Guard = #pointer_guard;

unsafe fn unsafe_to_value(self) -> #vm_result<(#value, Self::Guard)> {
let (shared, guard) = #shared::from_mut(self);
#vm_result::Ok((#value::from(shared), guard))
unsafe fn unsafe_to_value(self) -> #vm_result<(#value, Self::Guard)> {
let (shared, guard) = #shared::from_mut(self);
#vm_result::Ok((#value::from(shared), guard))
}
}
}
})
} else {
None
};

Ok(quote! {
#install_with
#impl_named
#any
})
}
5 changes: 5 additions & 0 deletions crates/rune-macros/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ pub(crate) struct TypeAttr {
pub(crate) constructor: bool,
/// Parsed documentation.
pub(crate) docs: Vec<syn::Expr>,
/// Indicates that this is a builtin type, so don't generate an `Any`
/// implementation for it.
pub(crate) builtin: Option<Span>,
}

/// Parsed variant attributes.
Expand Down Expand Up @@ -450,6 +453,8 @@ impl Context {
attr.install_with = Some(parse_path_compat(meta.input)?);
} else if meta.path == CONSTRUCTOR {
attr.constructor = true;
} else if meta.path == BUILTIN {
attr.builtin = Some(meta.path.span());
} else {
return Err(syn::Error::new_spanned(
&meta.path,
Expand Down
1 change: 1 addition & 0 deletions crates/rune-macros/src/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub const MODULE: Symbol = Symbol("module");
pub const INSTALL_WITH: Symbol = Symbol("install_with");

pub const CONSTRUCTOR: Symbol = Symbol("constructor");
pub const BUILTIN: Symbol = Symbol("builtin");
pub const GET: Symbol = Symbol("get");
pub const SET: Symbol = Symbol("set");
pub const COPY: Symbol = Symbol("copy");
Expand Down
2 changes: 2 additions & 0 deletions crates/rune/src/modules/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ fn is_readable(value: Value) -> bool {
Value::RangeToInclusive(value) => value.is_readable(),
Value::RangeTo(value) => value.is_readable(),
Value::Range(value) => value.is_readable(),
Value::ControlFlow(value) => value.is_readable(),
Value::Future(value) => value.is_readable(),
Value::Stream(value) => value.is_readable(),
Value::Generator(value) => value.is_readable(),
Expand Down Expand Up @@ -132,6 +133,7 @@ fn is_writable(value: Value) -> bool {
Value::RangeToInclusive(value) => value.is_writable(),
Value::RangeTo(value) => value.is_writable(),
Value::Range(value) => value.is_writable(),
Value::ControlFlow(value) => value.is_writable(),
Value::Future(value) => value.is_writable(),
Value::Stream(value) => value.is_writable(),
Value::Generator(value) => value.is_writable(),
Expand Down
24 changes: 22 additions & 2 deletions crates/rune/src/modules/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use core::cmp::Ordering;

use crate as rune;
use crate::runtime::{
EnvProtocolCaller, Function, Generator, GeneratorState, Iterator, Protocol, Range, RangeFrom,
RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Value, Vm, VmResult,
ControlFlow, EnvProtocolCaller, Function, Generator, GeneratorState, Iterator, Protocol, Range,
RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Value, Vm, VmResult,
};
use crate::{ContextError, Module};

Expand Down Expand Up @@ -285,6 +285,26 @@ pub fn module() -> Result<Module, ContextError> {
m.function_meta(Range::cmp__meta)?;
}

{
m.ty::<ControlFlow>()?.docs([
" Used to tell an operation whether it should exit early or go on as usual.",
"",
" This acts as the basis of the [`TRY`] protocol in Rune.",
"",
" [`TRY`]: crate::Protocol::TRY",
"",
"# Examples",
"",
"```rune",
"use std::ops::ControlFlow;",
"",
"let c = ControlFlow::Continue(42);",
"assert_eq!(c.0, 42);",
"assert_eq!(c, ControlFlow::Continue(42));",
"```",
]);
}

m.ty::<Function>()?.docs([
"The type of a function in Rune.",
"",
Expand Down
23 changes: 22 additions & 1 deletion crates/rune/src/modules/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::fmt;

use crate as rune;
use crate::runtime::{Formatter, Function, Iterator, Panic, Shared, Value, VmResult};
use crate::runtime::{ControlFlow, Formatter, Function, Iterator, Panic, Shared, Value, VmResult};
use crate::{ContextError, Module};

/// Construct the `std::option` module.
Expand All @@ -25,6 +25,7 @@ pub fn module() -> Result<Module, ContextError> {
module.function_meta(ok_or)?;
module.function_meta(ok_or_else)?;
module.function_meta(into_iter)?;
module.function_meta(option_try__meta)?;
Ok(module)
}

Expand Down Expand Up @@ -397,3 +398,23 @@ fn ok_or_else(this: Option<Value>, err: Function) -> VmResult<Result<Value, Valu
None => VmResult::Ok(Err(vm_try!(err.call(())))),
}
}

/// Using [`Option`] with the try protocol.
///
/// # Examples
///
/// ```rune
/// fn maybe_add_one(value) {
/// Some(value? + 1)
/// }
///
/// assert_eq!(maybe_add_one(Some(4)), Some(5));
/// assert_eq!(maybe_add_one(None), None);
/// ```
#[rune::function(keep, instance, protocol = TRY)]
pub(crate) fn option_try(this: Option<Value>) -> ControlFlow {
match this {
Some(value) => ControlFlow::Continue(value),
None => ControlFlow::Break(Value::Option(Shared::new(None))),
}
}
Loading

0 comments on commit e4b6798

Please sign in to comment.