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
19 changes: 19 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@

# Changelog

## Unreleased

### Candid

* Breaking changes:
+ `pp_args` and `pp_init_args` noew require a `&[ArgType]` parameter. The `pp_rets` function has been added, with the signature of the old `pp_args`.

### candid_parser

* Breaking changes:
+ The `args` field in both `FuncType` and `IDLInitArgs` now have type `Vec<IDLArgType>`.

* Non-breaking changes:
+ Supports parsing the arguments' names for `func` and `service` (init args).

### candid_derive

* Keeps argument names for Rust functions.

## 2025-05-15

### Candid 0.10.14
Expand Down
9 changes: 7 additions & 2 deletions rust/bench/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,16 @@ fn nns() -> BenchResult {
let (env, serv) = nns_did.load().unwrap();
let args = candid_parser::parse_idl_args(motion_proposal).unwrap();
let serv = serv.unwrap();
let arg_tys = &env.get_method(&serv, "manage_neuron").unwrap().args;
let method = &env.get_method(&serv, "manage_neuron").unwrap();
let arg_tys = method
.args
.iter()
.map(|arg| arg.typ.clone())
.collect::<Vec<_>>();
drop(_p);
let bytes = {
let _p = bench_scope("1. Encoding");
args.to_bytes_with_types(&env, arg_tys).unwrap()
args.to_bytes_with_types(&env, &arg_tys).unwrap()
};
{
let _p = bench_scope("2. Decoding");
Expand Down
7 changes: 5 additions & 2 deletions rust/candid/src/binary_parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::types::internal::{Field, Function, Label, Type, TypeInner};
use crate::types::internal::{ArgType, Field, Function, Label, Type, TypeInner};
use crate::types::{FuncMode, TypeEnv};
use anyhow::{anyhow, Context, Result};
use binread::io::{Read, Seek};
Expand Down Expand Up @@ -201,7 +201,10 @@ impl ConsType {
let mut args = Vec::new();
let mut rets = Vec::new();
for arg in &f.args {
args.push(arg.to_type(len)?);
args.push(ArgType {
name: None,
typ: arg.to_type(len)?,
});
}
for ret in &f.rets {
rets.push(ret.to_type(len)?);
Expand Down
25 changes: 20 additions & 5 deletions rust/candid/src/pretty/candid.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::pretty::utils::*;
use crate::types::{Field, FuncMode, Function, Label, SharedLabel, Type, TypeEnv, TypeInner};
use crate::types::{
ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeEnv, TypeInner,
};
use pretty::RcDoc;

static KEYWORDS: [&str; 30] = [
Expand Down Expand Up @@ -149,18 +151,31 @@ fn pp_fields(fs: &[Field], is_variant: bool) -> RcDoc {

pub fn pp_function(func: &Function) -> RcDoc {
let args = pp_args(&func.args);
let rets = pp_args(&func.rets);
let rets = pp_rets(&func.rets);
let modes = pp_modes(&func.modes);
args.append(" ->")
.append(RcDoc::space())
.append(rets.append(modes))
.nest(INDENT_SPACE)
}

pub fn pp_args(args: &[Type]) -> RcDoc {
let doc = concat(args.iter().map(pp_ty), ",");
pub fn pp_args(args: &[ArgType]) -> RcDoc {
let args = args.iter().map(|arg| {
if let Some(name) = &arg.name {
pp_text(name).append(kwd(" :")).append(pp_ty(&arg.typ))
} else {
pp_ty(&arg.typ)
}
});
let doc = concat(args, ",");
enclose("(", doc, ")")
}

pub fn pp_rets(rets: &[Type]) -> RcDoc {
let doc = concat(rets.iter().map(pp_ty), ",");
enclose("(", doc, ")")
}

pub fn pp_mode(mode: &FuncMode) -> RcDoc {
match mode {
FuncMode::Oneway => RcDoc::text("oneway"),
Expand Down Expand Up @@ -205,7 +220,7 @@ fn pp_actor(ty: &Type) -> RcDoc {
}
}

pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [Type]) -> RcDoc<'a> {
pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [ArgType]) -> RcDoc<'a> {
pp_defs(env).append(pp_args(args))
}
pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
Expand Down
7 changes: 5 additions & 2 deletions rust/candid/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,16 @@ impl TypeSerialize {
}
}
TypeInner::Func(ref func) => {
for ty in func.args.iter().chain(func.rets.iter()) {
for ty in &func.args {
self.build_type(&ty.typ)?;
}
for ty in &func.rets {
self.build_type(ty)?;
}
sleb128_encode(&mut buf, Opcode::Func as i64)?;
leb128_encode(&mut buf, func.args.len() as u64)?;
for ty in &func.args {
self.encode(&mut buf, ty)?;
self.encode(&mut buf, &ty.typ)?;
}
leb128_encode(&mut buf, func.rets.len() as u64)?;
for ty in &func.rets {
Expand Down
67 changes: 54 additions & 13 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,31 @@ impl TypeContainer {
}
TypeInner::Func(func) => TypeInner::Func(Function {
modes: func.modes.clone(),
args: func.args.iter().map(|arg| self.go(arg)).collect(),
args: func
.args
.iter()
.map(|arg| ArgType {
name: arg.name.clone(),
typ: self.go(&arg.typ),
})
.collect(),
rets: func.rets.iter().map(|arg| self.go(arg)).collect(),
}),
TypeInner::Service(serv) => TypeInner::Service(
serv.iter()
.map(|(id, t)| (id.clone(), self.go(t)))
.collect(),
),
TypeInner::Class(inits, ref ty) => {
TypeInner::Class(inits.iter().map(|t| self.go(t)).collect(), self.go(ty))
}
TypeInner::Class(inits, ref ty) => TypeInner::Class(
inits
.iter()
.map(|t| ArgType {
name: t.name.clone(),
typ: self.go(&t.typ),
})
.collect(),
self.go(ty),
),
t => t.clone(),
}
.into()
Expand Down Expand Up @@ -196,7 +210,7 @@ pub enum TypeInner {
Variant(Vec<Field>),
Func(Function),
Service(Vec<(String, Type)>),
Class(Vec<Type>, Type),
Class(Vec<ArgType>, Type),
Principal,
Future,
}
Expand Down Expand Up @@ -278,7 +292,14 @@ impl Type {
let func = func.clone();
Func(Function {
modes: func.modes,
args: func.args.into_iter().map(|t| t.subst(tau)).collect(),
args: func
.args
.into_iter()
.map(|t| ArgType {
name: t.name,
typ: t.typ.subst(tau),
})
.collect(),
rets: func.rets.into_iter().map(|t| t.subst(tau)).collect(),
})
}
Expand All @@ -287,7 +308,15 @@ impl Type {
.map(|(meth, ty)| (meth.clone(), ty.subst(tau)))
.collect(),
),
Class(args, ty) => Class(args.iter().map(|t| t.subst(tau)).collect(), ty.subst(tau)),
Class(args, ty) => Class(
args.iter()
.map(|t| ArgType {
name: t.name.clone(),
typ: t.typ.subst(tau),
})
.collect(),
ty.subst(tau),
),
_ => return self.clone(),
}
.into()
Expand Down Expand Up @@ -353,7 +382,12 @@ pub fn text_size(t: &Type, limit: i32) -> Result<i32, ()> {
let mut cnt = mode + 6;
let mut limit = limit - cnt;
for t in &func.args {
cnt += text_size(t, limit)?;
if let Some(name) = t.name.as_ref() {
let id_size = name.len() as i32;
cnt += id_size + text_size(&t.typ, limit - id_size - 3)? + 3;
} else {
cnt += text_size(&t.typ, limit)?;
}
limit -= cnt;
}
for t in &func.rets {
Expand Down Expand Up @@ -510,9 +544,16 @@ pub enum FuncMode {
#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct Function {
pub modes: Vec<FuncMode>,
pub args: Vec<Type>,
pub args: Vec<ArgType>,
pub rets: Vec<Type>,
}

#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct ArgType {
pub name: Option<String>,
pub typ: Type,
}

#[cfg(feature = "printer")]
impl fmt::Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand All @@ -539,16 +580,16 @@ impl Function {
/// `func!((u8, &str) -> (Nat) query)` expands to `Type(Rc::new(TypeInner::Func(...)))`
macro_rules! func {
( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![] }))
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![] }))
};
( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) query ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] }))
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] }))
};
( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) composite_query ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] }))
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] }))
};
( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) oneway ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] }))
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] }))
};
}
#[macro_export]
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod type_env;
pub mod value;

pub use self::internal::{
get_type, Field, FuncMode, Function, Label, SharedLabel, Type, TypeId, TypeInner,
get_type, ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeId, TypeInner,
};
pub use type_env::TypeEnv;

Expand Down
44 changes: 36 additions & 8 deletions rust/candid/src/types/subtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,18 @@ fn subtype_(
if f1.modes != f2.modes {
return Err(Error::msg("Function mode mismatch"));
}
let args1 = to_tuple(&f1.args);
let args2 = to_tuple(&f2.args);
let f1_args = f1
.args
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let f2_args = f2
.args
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let args1 = to_tuple(&f1_args);
let args2 = to_tuple(&f2_args);
let rets1 = to_tuple(&f1.rets);
let rets2 = to_tuple(&f2.rets);
subtype_(report, gamma, env, &args2, &args1)
Expand Down Expand Up @@ -212,17 +222,35 @@ pub fn equal(gamma: &mut Gamma, env: &TypeEnv, t1: &Type, t2: &Type) -> Result<(
if f1.modes != f2.modes {
return Err(Error::msg("Function mode mismatch"));
}
let args1 = to_tuple(&f1.args);
let args2 = to_tuple(&f2.args);
let f1_args = f1
.args
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let f2_args = f2
.args
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let args1 = to_tuple(&f1_args);
let args2 = to_tuple(&f2_args);
let rets1 = to_tuple(&f1.rets);
let rets2 = to_tuple(&f2.rets);
equal(gamma, env, &args1, &args2).context("Mismatch in function input type")?;
equal(gamma, env, &rets1, &rets2).context("Mismatch in function return type")?;
Ok(())
}
(Class(init1, ty1), Class(init2, ty2)) => {
let init_1 = to_tuple(init1);
let init_2 = to_tuple(init2);
let init1_typ = init1
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let init2_typ = init2
.iter()
.map(|arg| arg.typ.clone())
.collect::<std::vec::Vec<_>>();
let init_1 = to_tuple(&init1_typ);
let init_2 = to_tuple(&init2_typ);
equal(gamma, env, &init_1, &init_2).context(format!(
"Mismatch in init args: {} and {}",
pp_args(init1),
Expand Down Expand Up @@ -277,7 +305,7 @@ fn to_tuple(args: &[Type]) -> Type {
.into()
}
#[cfg(not(feature = "printer"))]
fn pp_args(args: &[crate::types::Type]) -> String {
fn pp_args(args: &[crate::types::ArgType]) -> String {
use std::fmt::Write;
let mut s = String::new();
write!(&mut s, "(").unwrap();
Expand All @@ -288,7 +316,7 @@ fn pp_args(args: &[crate::types::Type]) -> String {
s
}
#[cfg(feature = "printer")]
fn pp_args(args: &[crate::types::Type]) -> String {
fn pp_args(args: &[crate::types::ArgType]) -> String {
use crate::pretty::candid::pp_args;
pp_args(args).pretty(80).to_string()
}
Loading
Loading