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
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* [BREAKING]: type representation was optimized to improve performance:
+ In `Type::Var(var)` `var` now has type `TypeKey` instead of `String`. Calling `var.as_str()` returns `&str` and `var.to_string()` returns a `String`. The string representation of indexed variables remains `table{index}` to maintain compatibility with previous versions.
+ `TypeEnv` now contains a `HashMap` instead of `BTreeMap`. Code that relied on the iteration order of the map (e.g. `env.0.iter()`) should make use of the newly added `TypeEnv::to_sorted_iter()` method which returns types sorted by their keys.
+ The `args` field of the `candid::types::internal::Function` struct now is a `Vec<ArgType>` instead of `Vec<Type>`, to preserve argument names.
+ The `args` and `rets` fields of the `candid::types::internal::Function` struct now are a `Vec<ArgType>` instead of `Vec<Type>`, to preserve names.
+ The `TypeInner::Class` variant now takes `Vec<ArgType>` instead of `Vec<Type>` as its first parameter, to preserve argument names.

* [BREAKING]: Removed the `candid::pretty::concat` function
Expand All @@ -16,7 +16,7 @@
* Non-breaking changes:
+ Added `pp_named_args`, `pp_named_init_args` in `pretty::candid` module.
+ The `JavaScript` `didc` target now exports its generated IDL type objects.
+ The `JavaScript` and `TypeScript` `didc` targets now export `idlService` and `idlInitArgs` (non-factory-function altneratives to `idlFactory` and `init`).
+ The `JavaScript` and `TypeScript` `didc` targets now export `idlService` and `idlInitArgs` (non-factory-function alternatives to `idlFactory` and `init`).
+ fix: subtyping and coercion rules for optional types
+ fix: coercion of values into nested optional types
+ fix: values of types `reserved` at any context do not coerce into values of type `null`
Expand All @@ -26,6 +26,7 @@

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

* Non-breaking changes:
+ Supports parsing the arguments' names for `func` and `service` (init args).
Expand Down
5 changes: 4 additions & 1 deletion rust/candid/src/binary_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ impl ConsType {
});
}
for ret in &f.rets {
rets.push(ret.to_type(len)?);
rets.push(ArgType {
name: None,
typ: ret.to_type(len)?,
});
}
TypeInner::Func(Function {
modes: f.ann.iter().map(|x| x.inner.clone()).collect(),
Expand Down
7 changes: 1 addition & 6 deletions rust/candid/src/pretty/candid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ fn pp_fields(fs: &[Field], is_variant: bool) -> RcDoc<'_> {

pub fn pp_function(func: &Function) -> RcDoc<'_> {
let args = pp_named_args(&func.args);
let rets = pp_rets(&func.rets);
let rets = pp_named_args(&func.rets);
let modes = pp_modes(&func.modes);
args.append(" ->")
.append(RcDoc::space())
Expand Down Expand Up @@ -185,11 +185,6 @@ pub fn pp_args(args: &[Type]) -> RcDoc<'_> {
sep_enclose(args.iter().map(pp_ty), ",", "(", ")")
}

/// Pretty-prints return types in the form of `(type1, type2)`.
pub fn pp_rets(args: &[Type]) -> RcDoc<'_> {
pp_args(args)
}

pub fn pp_mode(mode: &FuncMode) -> RcDoc<'_> {
match mode {
FuncMode::Oneway => RcDoc::text("oneway"),
Expand Down
4 changes: 2 additions & 2 deletions rust/candid/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ impl TypeSerialize {
self.build_type(&ty.typ)?;
}
for ty in &func.rets {
self.build_type(ty)?;
self.build_type(&ty.typ)?;
}
sleb128_encode(&mut buf, Opcode::Func as i64)?;
leb128_encode(&mut buf, func.args.len() as u64)?;
Expand All @@ -348,7 +348,7 @@ impl TypeSerialize {
}
leb128_encode(&mut buf, func.rets.len() as u64)?;
for ty in &func.rets {
self.encode(&mut buf, ty)?;
self.encode(&mut buf, &ty.typ)?;
}
leb128_encode(&mut buf, func.modes.len() as u64)?;
for m in &func.modes {
Expand Down
30 changes: 22 additions & 8 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,14 @@ impl TypeContainer {
typ: self.go(&arg.typ),
})
.collect(),
rets: func.rets.iter().map(|arg| self.go(arg)).collect(),
rets: func
.rets
.iter()
.map(|arg| ArgType {
name: arg.name.clone(),
typ: self.go(&arg.typ),
})
.collect(),
}),
TypeInner::Service(serv) => TypeInner::Service(
serv.iter()
Expand Down Expand Up @@ -307,7 +314,14 @@ impl Type {
typ: t.typ.subst(tau),
})
.collect(),
rets: func.rets.into_iter().map(|t| t.subst(tau)).collect(),
rets: func
.rets
.into_iter()
.map(|t| ArgType {
name: t.name,
typ: t.typ.subst(tau),
})
.collect(),
})
}
Service(serv) => Service(
Expand Down Expand Up @@ -398,7 +412,7 @@ pub fn text_size(t: &Type, limit: i32) -> Result<i32, ()> {
limit -= cnt;
}
for t in &func.rets {
cnt += text_size(t, limit)?;
cnt += text_size(&t.typ, limit)?;
limit -= cnt;
}
cnt
Expand Down Expand Up @@ -552,7 +566,7 @@ pub enum FuncMode {
pub struct Function {
pub modes: Vec<FuncMode>,
pub args: Vec<ArgType>,
pub rets: Vec<Type>,
pub rets: Vec<ArgType>,
}

#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
Expand Down Expand Up @@ -587,16 +601,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()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), 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()),*].into_iter().map(|ret| $crate::types::ArgType { name: None, typ: ret }).collect(), modes: vec![] }))
};
( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) 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] }))
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()),*].into_iter().map(|ret| $crate::types::ArgType { name: None, typ: ret }).collect(), 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()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), 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()),*].into_iter().map(|ret| $crate::types::ArgType { name: None, typ: ret }).collect(), 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()),*].into_iter().map(|arg| $crate::types::ArgType { name: None, typ: arg }).collect(), 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()),*].into_iter().map(|ret| $crate::types::ArgType { name: None, typ: ret }).collect(), modes: vec![$crate::types::FuncMode::Oneway] }))
};
}
#[macro_export]
Expand Down
48 changes: 10 additions & 38 deletions rust/candid/src/types/subtype.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::internal::{find_type, Field, Label, Type, TypeInner};
use crate::types::TypeEnv;
use crate::types::{ArgType, TypeEnv};
use crate::{Error, Result};
use anyhow::Context;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -129,18 +129,8 @@ fn subtype_(
if f1.modes != f2.modes {
return Err(Error::msg("Function mode mismatch"));
}
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 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 @@ -222,35 +212,17 @@ 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 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 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 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);
let init_1 = to_tuple(init1);
let init_2 = to_tuple(init2);
equal(gamma, env, &init_1, &init_2).context(format!(
"Mismatch in init args: {} and {}",
pp_args(init1),
Expand Down Expand Up @@ -292,13 +264,13 @@ where
}
}

fn to_tuple(args: &[Type]) -> Type {
fn to_tuple(args: &[ArgType]) -> Type {
TypeInner::Record(
args.iter()
.enumerate()
.map(|(i, ty)| Field {
.map(|(i, arg)| Field {
id: Label::Id(i as u32).into(),
ty: ty.clone(),
ty: arg.typ.clone(),
})
.collect(),
)
Expand Down
4 changes: 2 additions & 2 deletions rust/candid_derive/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub(crate) fn export_service(path: Option<TokenStream>) -> TokenStream {
#doc_storage
let mut args: Vec<ArgType> = Vec::new();
#(#args)*
let mut rets: Vec<Type> = Vec::new();
let mut rets: Vec<ArgType> = Vec::new();
#(#rets)*
let func = Function { args, rets, modes: #modes };
service.push((#name.to_string(), TypeInner::Func(func).into()));
Expand Down Expand Up @@ -195,7 +195,7 @@ fn generate_arg(name: TokenStream, (arg_name, ty): &(Option<String>, String)) ->
fn generate_ret(name: TokenStream, ty: &str) -> TokenStream {
let ty = syn::parse_str::<Type>(ty).unwrap();
quote! {
#name.push(env.add::<#ty>());
#name.push(ArgType { name: None, typ: env.add::<#ty>() });
}
}

Expand Down
8 changes: 5 additions & 3 deletions rust/candid_parser/src/bindings/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ pub fn chase_type<'a>(
}
Func(f) => {
let args = f.args.iter().map(|arg| &arg.typ);
for ty in args.clone().chain(f.rets.iter()) {
let rets = f.rets.iter().map(|ret| &ret.typ);
for ty in args.chain(rets) {
chase_type(seen, res, env, ty)?;
}
}
Expand Down Expand Up @@ -117,7 +118,7 @@ pub fn chase_def_use<'a>(
}
for (i, arg) in func.rets.iter().enumerate() {
let mut used = Vec::new();
chase_type(&mut BTreeSet::new(), &mut used, env, arg)?;
chase_type(&mut BTreeSet::new(), &mut used, env, &arg.typ)?;
for var in used {
res.entry(var.to_string())
.or_insert_with(Vec::new)
Expand Down Expand Up @@ -162,7 +163,8 @@ pub fn infer_rec<'a>(_env: &'a TypeEnv, def_list: &'a [&'a str]) -> Result<BTree
}
Func(f) => {
let args = f.args.iter().map(|arg| &arg.typ);
for ty in args.clone().chain(f.rets.iter()) {
let rets = f.rets.iter().map(|ret| &ret.typ);
for ty in args.chain(rets) {
go(seen, res, _env, ty)?;
}
}
Expand Down
18 changes: 11 additions & 7 deletions rust/candid_parser/src/bindings/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,20 @@ fn pp_fields(fs: &[Field]) -> RcDoc<'_> {

fn pp_function(func: &Function) -> RcDoc<'_> {
let args = pp_args(&func.args);
let rets = pp_rets(&func.rets);
let rets = pp_args(&func.rets);
let modes = pp_modes(&func.modes);
sep_enclose([args, rets, modes], ",", "(", ")").nest(INDENT_SPACE)
}

fn pp_args(args: &[ArgType]) -> RcDoc<'_> {
let args = args.iter().map(|arg| pp_ty(&arg.typ));
sep_enclose(args, ",", "[", "]")
pp_types(args.iter().map(|arg| &arg.typ))
}

fn pp_rets(args: &[Type]) -> RcDoc<'_> {
sep_enclose(args.iter().map(pp_ty), ",", "[", "]")
fn pp_types<'a, T>(types: T) -> RcDoc<'a>
where
T: Iterator<Item = &'a Type>,
{
sep_enclose(types.map(pp_ty), ",", "[", "]")
}

fn pp_modes(modes: &[candid::types::FuncMode]) -> RcDoc<'_> {
Expand Down Expand Up @@ -275,7 +277,7 @@ pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
.append(";");

let idl_init_args = str("export const idlInitArgs = ")
.append(pp_rets(init_types))
.append(pp_types(init_types.iter()))
.append(";");

let idl_factory_return = kwd("return").append(actor).append(";");
Expand All @@ -286,7 +288,9 @@ pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
let init_defs = chase_types(env, init_types).unwrap();
let init_recs = infer_rec(env, &init_defs).unwrap();
let init_defs_doc = pp_defs(env, &init_defs, &init_recs, false);
let init_doc = kwd("return").append(pp_rets(init_types)).append(";");
let init_doc = kwd("return")
.append(pp_types(init_types.iter()))
.append(";");
let init_doc = init_defs_doc.append(init_doc);
let init_doc =
str("export const init = ({ IDL }) => ").append(enclose_space("{", init_doc, "};"));
Expand Down
13 changes: 7 additions & 6 deletions rust/candid_parser/src/bindings/motoko.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,17 @@ fn pp_args(args: &[ArgType]) -> RcDoc<'_> {
}
}

fn pp_rets(args: &[Type]) -> RcDoc<'_> {
match args {
fn pp_rets(rets: &[ArgType]) -> RcDoc<'_> {
match rets {
[ty] => {
if is_tuple(ty) {
enclose("(", pp_ty(ty), ")")
let typ = &ty.typ;
if is_tuple(typ) {
enclose("(", pp_ty(typ), ")")
} else {
pp_ty(ty)
pp_ty(typ)
}
}
_ => sep_enclose(args.iter().map(pp_ty), ",", "(", ")"),
_ => sep_enclose(rets.iter().map(|ret| pp_ty(&ret.typ)), ",", "(", ")"),
}
}

Expand Down
Loading