Skip to content

Commit

Permalink
fix: Prevent zero-argument functions from being created in Rust
Browse files Browse the repository at this point in the history
These cannot be called from the gluon side, instead a one argument
function that takes `()` should be used.

Fixes gluon-lang#873
  • Loading branch information
Marwes committed Sep 14, 2020
1 parent 1c8a1c0 commit e91ea06
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 48 deletions.
20 changes: 11 additions & 9 deletions base/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3230,7 +3230,7 @@ where
type Context = NullInterner;

fn context(&mut self) -> &mut Self::Context {
&mut NullInterner
NullInterner::new()
}

fn visit(&mut self, typ: &T) -> Option<T>
Expand Down Expand Up @@ -3816,13 +3816,15 @@ where

pub type SharedInterner<Id, T> = TypeCache<Id, T>;

pub struct NullInternerInner;
// Workaround since &mut NullInterner does not get promoted to a `&'static mut NullInterner` but
// `&mut []` does
pub type NullInterner = [NullInternerInner; 0];
#[derive(Default)]
pub struct NullInterner;

#[allow(non_upper_case_globals)]
pub const NullInterner: NullInterner = [];
impl NullInterner {
pub fn new() -> &'static mut NullInterner {
// SAFETY NullInterner is zero-sized
unsafe { &mut *(&mut NullInterner as *mut _) }
}
}

impl<Id, T> TypeContext<Id, T> for NullInterner
where
Expand Down Expand Up @@ -4267,7 +4269,7 @@ where
type Context = NullInterner;

fn context(&mut self) -> &mut Self::Context {
&mut NullInterner
NullInterner::new()
}

fn visit(&mut self, typ: &T) -> Option<T>
Expand Down Expand Up @@ -4304,7 +4306,7 @@ where
type Context = NullInterner;

fn context(&mut self) -> &mut Self::Context {
&mut NullInterner
NullInterner::new()
}

fn visit(&mut self, typ: &T) -> Option<T>
Expand Down
13 changes: 7 additions & 6 deletions completion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ where
self.stack.insert(id.name.clone(), id.typ.clone());
}
Pattern::Record { typ, fields, .. } => {
let unaliased = resolve::remove_aliases(&self.env, &mut NullInterner, typ.clone());
let unaliased =
resolve::remove_aliases(&self.env, NullInterner::new(), typ.clone());
for field in &**fields {
match field {
PatternField::Type { name } => {
Expand Down Expand Up @@ -359,7 +360,7 @@ where
if ident.span.containment(self.pos) == Ordering::Equal {
let record_type = resolve::remove_aliases(
&crate::base::ast::EmptyEnv::default(),
&mut NullInterner,
NullInterner::new(),
record_type.clone(),
);
let either = record_type
Expand Down Expand Up @@ -1303,7 +1304,7 @@ impl SuggestionQuery {
Pattern::Constructor(ref id, _) | Pattern::Ident(ref id) => id.as_ref(),
Pattern::Record { ref fields, .. } => {
if let Ok(typ) = expr.try_type_of(&env) {
let typ = resolve::remove_aliases(env, &mut NullInterner, typ);
let typ = resolve::remove_aliases(env, NullInterner::new(), typ);
self.suggest_fields_of_type(&mut result, fields, "", &typ);
}
""
Expand All @@ -1325,7 +1326,7 @@ impl SuggestionQuery {
Match::Expr(context) => match context.value {
Expr::Projection(ref expr, _, _) => {
if let Ok(typ) = expr.try_type_of(&env) {
let typ = resolve::remove_aliases(&env, &mut NullInterner, typ);
let typ = resolve::remove_aliases(&env, NullInterner::new(), typ);
let id = ident.as_ref();

let iter = typ
Expand Down Expand Up @@ -1368,7 +1369,7 @@ impl SuggestionQuery {
},
..
}) => {
let typ = resolve::remove_aliases_cow(env, &mut NullInterner, typ);
let typ = resolve::remove_aliases_cow(env, NullInterner::new(), typ);
self.suggest_fields_of_type(
&mut result,
fields,
Expand Down Expand Up @@ -1409,7 +1410,7 @@ impl SuggestionQuery {
Match::Pattern(pattern) => match pattern.value {
Pattern::Record { ref fields, .. } => {
if let Ok(typ) = pattern.try_type_of(env) {
let typ = resolve::remove_aliases(env, &mut NullInterner, typ);
let typ = resolve::remove_aliases(env, NullInterner::new(), typ);
self.suggest_fields_of_type(&mut result, fields, "", &typ);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-fail/run_expr_str_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ fn main() {
let vm = new_vm();

let _ = vm.run_expr::<&str>("", r#" "test" "#);
//~^ the trait bound `for<'value> &str: gluon::gluon_vm::api::Getable<'_, 'value>` is not satisfied [E0277]
//~^ the trait bound `for<'value> &str: Getable<'_, 'value>` is not satisfied [E0277]
}
97 changes: 65 additions & 32 deletions vm/src/api/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,14 @@ where
}

macro_rules! vm_function_impl {
([$($f:tt)*] $($args:ident),*) => {
([$($f:tt)*] $($args:ident),* -> $ret: ident, $ret_ty: ty) => {

impl <'vm, $($args,)* R> VmFunction<'vm> for $($f)* ($($args),*) -> R
impl <'vm, $($args,)* $ret> VmFunction<'vm> for $($f)* ($($args),*) -> $ret_ty
where $($args: Getable<'vm, 'vm> + 'vm,)*
R: AsyncPushable<'vm> + VmType + 'vm
$ret: AsyncPushable<'vm> + VmType + 'vm,
$ret_ty: AsyncPushable<'vm> + VmType + 'vm,
$ret::Type: Sized,
<$ret_ty as VmType>::Type: Sized,
{
#[allow(non_snake_case, unused_mut, unused_assignments, unused_variables, unused_unsafe)]
fn unpack_and_call(&self, vm: &'vm Thread) -> Status {
Expand Down Expand Up @@ -341,49 +344,76 @@ where
}

macro_rules! make_vm_function {
($($args:ident),*) => (
impl <$($args: VmType,)* R: VmType> VmType for fn ($($args),*) -> R {
($($args:ident),*) => {
make_vm_function_inner!($($args),* -> R, R);
}
}
macro_rules! make_vm_function_inner {
($($args:ident),* -> $ret: ident, $ret_ty: ty) => (
impl <$($args: VmType,)* $ret: VmType> VmType for fn ($($args),*) -> $ret_ty
where
<$ret_ty as VmType>::Type: Sized,
<$ret as VmType>::Type: Sized,
{
#[allow(non_snake_case)]
type Type = fn ($($args::Type),*) -> R::Type;
type Type = fn ($($args::Type),*) -> <$ret_ty as VmType>::Type;

#[allow(non_snake_case)]
fn make_type(vm: &Thread) -> ArcType {
let args = vec![$(make_type::<$args>(vm)),*];
vm.global_env().type_cache().function(args, make_type::<R>(vm))
vm.global_env().type_cache().function(args, make_type::<$ret_ty>(vm))
}
}

vm_function_impl!([fn] $($args),*);
vm_function_impl!([dyn Fn] $($args),*);
vm_function_impl!([fn] $($args),* -> $ret, $ret_ty);
vm_function_impl!([dyn Fn] $($args),* -> $ret, $ret_ty);

impl <'vm, $($args,)* R: VmType> FunctionType for fn ($($args),*) -> R {
impl <'vm, $($args,)* $ret: VmType> FunctionType for fn ($($args),*) -> $ret_ty
where
$ret_ty: VmType,
<$ret_ty as VmType>::Type: Sized,
$ret::Type: Sized,
{
fn arguments() -> VmIndex {
count!($($args),*) + R::EXTRA_ARGS
count!($($args),*) + <$ret_ty as VmType>::EXTRA_ARGS
}
}

impl <'s, $($args,)* R: VmType> FunctionType for dyn Fn($($args),*) -> R + 's {
impl <'s, $($args,)* $ret: VmType> FunctionType for dyn Fn($($args),*) -> $ret_ty + 's
where
$ret_ty: VmType,
<$ret_ty as VmType>::Type: Sized,
$ret::Type: Sized,
{
fn arguments() -> VmIndex {
count!($($args),*) + R::EXTRA_ARGS
count!($($args),*) + <$ret_ty as VmType>::EXTRA_ARGS
}
}

impl <'s, $($args: VmType,)* R: VmType> VmType for dyn Fn($($args),*) -> R + 's {
type Type = fn ($($args::Type),*) -> R::Type;
impl <'s, $($args: VmType,)* $ret: VmType> VmType for dyn Fn($($args),*) -> $ret_ty + 's
where
$ret_ty: VmType,
<$ret_ty as VmType>::Type: Sized,
$ret::Type: Sized,
{
type Type = fn ($($args::Type),*) -> <$ret_ty as VmType>::Type;

#[allow(non_snake_case)]
fn make_type(vm: &Thread) -> ArcType {
<fn ($($args),*) -> R>::make_type(vm)
<fn ($($args),*) -> $ret_ty>::make_type(vm)
}
}

impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
where $($args: for<'vm> Pushable<'vm>,)*
T: VmRootInternal,
R: VmType + for<'x, 'value> Getable<'x, 'value>,
impl<T, $($args,)* $ret> Function<T, fn($($args),*) -> $ret_ty>
where
$($args: for<'vm> Pushable<'vm>,)*
T: VmRootInternal,
$ret: VmType + for<'x, 'value> Getable<'x, 'value>,
<$ret_ty as VmType>::Type: Sized,
$ret::Type: Sized,
{
#[allow(non_snake_case)]
pub fn call(&mut self $(, $args: $args)*) -> Result<R> {
pub fn call(&mut self $(, $args: $args)*) -> Result<$ret_ty> {
block_on_sync(future::lazy(|cx| {
match self.call_first(cx, $($args),*) {
Poll::Ready(r) => r,
Expand All @@ -393,17 +423,17 @@ impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
}

#[allow(non_snake_case)]
fn call_first(&self, cx: &mut task::Context<'_> $(, $args: $args)*) -> Poll<Result<R>> {
fn call_first(&self, cx: &mut task::Context<'_> $(, $args: $args)*) -> Poll<Result<$ret_ty>> {
let vm = self.value.vm();
let mut context = vm.current_context();
context.push(self.value.get_variant());
$(
$args.vm_push(&mut context)?;
)*
for _ in 0..R::EXTRA_ARGS {
for _ in 0..<$ret_ty as VmType>::EXTRA_ARGS {
0.vm_push(&mut context).unwrap();
}
let args = count!($($args),*) + R::EXTRA_ARGS;
let args = count!($($args),*) + <$ret_ty as VmType>::EXTRA_ARGS;
let context = ready!(vm.call_function(cx, context.into_owned(), args))?;
let mut context = context.unwrap();
let result = {
Expand All @@ -414,21 +444,24 @@ impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
Poll::Ready(result)
}

fn return_value(vm: &Thread, value: Variants) -> Result<R> {
Ok(R::from_value(vm, value))
fn return_value(vm: &Thread, value: Variants) -> Result<$ret_ty> {
Ok(<$ret_ty>::from_value(vm, value))
}
}

impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
where $($args: for<'vm> Pushable<'vm>,)*
T: VmRootInternal + Clone + Send,
R: VmType + for<'x, 'value> Getable<'x, 'value> + Send + Sync + 'static,
impl<T, $($args,)* $ret> Function<T, fn($($args),*) -> $ret_ty>
where
$($args: for<'vm> Pushable<'vm>,)*
T: VmRootInternal + Clone + Send,
$ret: VmType + for<'x, 'value> Getable<'x, 'value> + Send + Sync + 'static,
<$ret_ty as VmType>::Type: Sized,
$ret::Type: Sized,
{
#[allow(non_snake_case)]
pub async fn call_async(
&mut self
$(, $args: $args)*
) -> Result<R>
) -> Result<$ret_ty>
{
use crate::thread::Execute;
match future::lazy(|cx| self.call_first(cx, $($args),*)).await {
Expand All @@ -444,7 +477,7 @@ impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
)
}

make_vm_function!();
make_vm_function_inner!( -> R, crate::api::IO<R>);
make_vm_function!(A);
make_vm_function!(A, B);
make_vm_function!(A, B, C);
Expand Down

0 comments on commit e91ea06

Please sign in to comment.