Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b1d118c
refactor: move `IDL*` types into the candid crate, under the `parser`…
ilbertt Jun 17, 2025
6f0fd78
chore: update changelog and docs
ilbertt Jun 17, 2025
a9aee1c
refactor: map pretty error
ilbertt Jun 17, 2025
74f84c9
refactor: rename parser to syntax
ilbertt Jun 18, 2025
d1bf391
refactor: use `IDL*` structs in parser
ilbertt Jun 18, 2025
eb7c372
refactor: use IDLTypes in parser (motoko only)
ilbertt Jun 18, 2025
c60bc3f
refactor: use IDL* types in parser (wip)
ilbertt Jun 20, 2025
05511c7
fix: add unknown types
ilbertt Jun 20, 2025
e44d50a
fix: trace_type
ilbertt Jun 20, 2025
ea805e6
Merge remote-tracking branch 'origin/master' into luca/idl-types-in-p…
ilbertt Jun 20, 2025
b50529b
refactor: use `Error::msg`
ilbertt Jun 20, 2025
f39988b
fix: get_metadata actor fix
ilbertt Jun 20, 2025
a5d9ef2
fix: candid parser
ilbertt Jun 20, 2025
2d3887d
Merge remote-tracking branch 'origin/master' into luca/candid-idl-types
ilbertt Jun 20, 2025
3144ce6
refactor: remove "syntax" feature
ilbertt Jun 20, 2025
f097c4c
chore: update changelog
ilbertt Jun 20, 2025
b51c5b5
Merge remote-tracking branch 'origin/luca/candid-idl-types' into luca…
ilbertt Jun 20, 2025
a454e98
fix: candid derive
ilbertt Jun 20, 2025
7199405
fix: introduce knot for recursive types
ilbertt Jun 22, 2025
2e3d93f
fix: navigate knot types
ilbertt Jun 22, 2025
d729495
fix: visibility
ilbertt Jun 22, 2025
6aec277
refactor: group functions in env
ilbertt Jun 22, 2025
4d180a3
refactor: is_null helper
ilbertt Jun 22, 2025
8aacfa0
refactor: rename variables
ilbertt Jun 22, 2025
53f58ec
fix: types mapping
ilbertt Jun 22, 2025
2bc94c6
chore: update changelog
ilbertt Jun 22, 2025
eb9fad4
refactor: restore `TypeEnv` in the `typing` module
ilbertt Jun 23, 2025
e41b78b
style: clippy
ilbertt Jun 23, 2025
9a1291a
fix: use idl merged prog in candid_derive
ilbertt Jun 24, 2025
fe83168
fix: remove unused `get_id` function
ilbertt Jun 24, 2025
f9e1e8e
chore: update changelog and remove unused code
ilbertt Jun 24, 2025
09b3b44
perf: fix bench tests
ilbertt Jun 24, 2025
b571cb1
Merge remote-tracking branch 'origin/master' into luca/candid-idl-types
ilbertt Jun 25, 2025
106bdc1
Merge remote-tracking branch 'origin/master' into luca/candid-idl-types
ilbertt Jun 25, 2025
b93c4d8
style: remove unused import
ilbertt Jun 25, 2025
6694147
Merge branch 'master' into luca/candid-idl-types
ilbertt Jun 25, 2025
64088f2
Merge remote-tracking branch 'origin/luca/candid-idl-types' into luca…
ilbertt Jun 25, 2025
c70a084
test: update goldenfiles
ilbertt Jun 25, 2025
2f9fda6
feat: future and unknown variants in idl type
ilbertt Jun 25, 2025
241bc11
Merge remote-tracking branch 'origin/master' into luca/idl-types-in-p…
ilbertt Jun 25, 2025
8f45f21
refactor: remove redundant code
ilbertt Jun 26, 2025
7c00dc4
Merge branch 'master' into luca/idl-types-in-parser
ilbertt Jun 27, 2025
dbb1dd4
fix: remove future and unknown from idl types
ilbertt Jun 30, 2025
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
- `Binding`
- `IDLProg`
- `IDLInitArgs`
+ The `IDLMergedProg` struct has been added to support the parsing and bindings generation, to avoid using the `TypeEnv`.
+ The IDL types can now be converted to `Type` and using the `From` trait.
+ The `TypeEnv` struct now has a `as_idl_type` method to convert `Type` to `IDLType`.

### candid_parser

Expand All @@ -43,6 +46,10 @@
You must now use the `parse_idl_prog`, `parse_idl_init_args`, `parse_idl_type` and `parse_idl_types` functions to parse these types, respectively.
+ `pretty_parse` doesn't work anymore with the `IDLProg` and `IDLTypes` types. Use `pretty_parse_idl_prog` and `pretty_parse_idl_types` instead.
+ The `args` field in both `FuncType` and `IDLInitArgs` now have type `Vec<IDLArgType>`.
+ The bindings generators now use the IDL merged prog and types.
+ `check_file` and `pretty_check_file` now return a `(TypeEnv, Option<IDLType>, IDLMergedProg)` tuple, instead of `(TypeEnv, Option<Type>)`. Same for the `CandidSource.load` method.
+ `utils::get_metadata` now accepts only an `&IDLMergedProg` parameter.
+ The `chase_type`, `chase_actor`, `chase_def_use`, `chase_types` and `infer_rec` functions of the `bindings::analysis` module now accept an IDL merged prog and types as parameters.

* Non-breaking changes:
+ Supports parsing the arguments' names for `func` and `service` (init args).
Expand Down
2 changes: 1 addition & 1 deletion rust/bench/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ fn nns() -> BenchResult {
"#;
bench_fn(|| {
let _p = bench_scope("0. Parsing");
let (env, serv) = nns_did.load().unwrap();
let (env, serv, _) = nns_did.load().unwrap();
let args = candid_parser::parse_idl_args(motion_proposal).unwrap();
let serv = serv.unwrap();
let method = &env.get_method(&serv, "manage_neuron").unwrap();
Expand Down
100 changes: 50 additions & 50 deletions rust/candid/src/pretty/candid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::pretty::utils::*;
use crate::types::{
ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeEnv, TypeInner,
syntax::{Binding, FuncType, IDLArgType, IDLMergedProg, IDLType, PrimType, TypeField},
FuncMode, Label,
};
use pretty::RcDoc;

Expand Down Expand Up @@ -73,12 +74,8 @@ pub(crate) fn pp_text(id: &str) -> RcDoc {
RcDoc::text(ident_string(id))
}

pub fn pp_ty(ty: &Type) -> RcDoc {
pp_ty_inner(ty.as_ref())
}

pub fn pp_ty_inner(ty: &TypeInner) -> RcDoc {
use TypeInner::*;
fn pp_prim_ty(ty: &PrimType) -> RcDoc {
use PrimType::*;
match ty {
Null => str("null"),
Bool => str("bool"),
Expand All @@ -97,59 +94,62 @@ pub fn pp_ty_inner(ty: &TypeInner) -> RcDoc {
Text => str("text"),
Reserved => str("reserved"),
Empty => str("empty"),
Var(ref s) => str(s),
Principal => str("principal"),
Opt(ref t) => kwd("opt").append(pp_ty(t)),
Vec(ref t) if matches!(t.as_ref(), Nat8) => str("blob"),
Vec(ref t) => kwd("vec").append(pp_ty(t)),
Record(ref fs) => {
let t = Type(ty.clone().into());
if t.is_tuple() {
let tuple = concat(fs.iter().map(|f| pp_ty(&f.ty)), ";");
}
}

pub fn pp_ty(ty: &IDLType) -> RcDoc {
use IDLType::*;
match ty {
PrimT(ty) => pp_prim_ty(ty),
VarT(ref s) => str(s),
PrincipalT => str("principal"),
OptT(ref t) => kwd("opt").append(pp_ty(t)),
VecT(ref t) if matches!(t.as_ref(), PrimT(PrimType::Nat8)) => str("blob"),
VecT(ref t) => kwd("vec").append(pp_ty(t)),
RecordT(ref fs) => {
if ty.is_tuple() {
let tuple = concat(fs.iter().map(|f| pp_ty(&f.typ)), ";");
kwd("record").append(enclose_space("{", tuple, "}"))
} else {
kwd("record").append(pp_fields(fs, false))
}
}
Variant(ref fs) => kwd("variant").append(pp_fields(fs, true)),
Func(ref func) => kwd("func").append(pp_function(func)),
Service(ref serv) => kwd("service").append(pp_service(serv)),
Class(ref args, ref t) => {
VariantT(ref fs) => kwd("variant").append(pp_fields(fs, true)),
FuncT(ref func) => kwd("func").append(pp_function(func)),
ServT(ref serv) => kwd("service").append(pp_service(serv)),
ClassT(ref args, ref t) => {
let doc = pp_args(args).append(" ->").append(RcDoc::space());
match t.as_ref() {
Service(ref serv) => doc.append(pp_service(serv)),
Var(ref s) => doc.append(s),
IDLType::ServT(ref serv) => doc.append(pp_service(serv)),
IDLType::VarT(ref s) => doc.append(s),
_ => unreachable!(),
}
}
Knot(ref id) => RcDoc::text(format!("{id}")),
Unknown => str("unknown"),
Future => str("future"),
}
}

pub fn pp_label(id: &SharedLabel) -> RcDoc {
match &**id {
pub fn pp_label(id: &Label) -> RcDoc {
match id {
Label::Named(id) => pp_text(id),
Label::Id(_) | Label::Unnamed(_) => RcDoc::as_string(id),
}
}

pub(crate) fn pp_field(field: &Field, is_variant: bool) -> RcDoc {
let ty_doc = if is_variant && *field.ty == TypeInner::Null {
pub(crate) fn pp_field(field: &TypeField, is_variant: bool) -> RcDoc {
let ty_doc = if is_variant && field.typ.is_null() {
RcDoc::nil()
} else {
kwd(" :").append(pp_ty(&field.ty))
kwd(" :").append(pp_ty(&field.typ))
};
pp_label(&field.id).append(ty_doc)
pp_label(&field.label).append(ty_doc)
}

fn pp_fields(fs: &[Field], is_variant: bool) -> RcDoc {
fn pp_fields(fs: &[TypeField], is_variant: bool) -> RcDoc {
let fields = concat(fs.iter().map(|f| pp_field(f, is_variant)), ";");
enclose_space("{", fields, "}")
}

pub fn pp_function(func: &Function) -> RcDoc {
pub fn pp_function(func: &FuncType) -> RcDoc {
let args = pp_args(&func.args);
let rets = pp_rets(&func.rets);
let modes = pp_modes(&func.modes);
Expand All @@ -159,7 +159,7 @@ pub fn pp_function(func: &Function) -> RcDoc {
.nest(INDENT_SPACE)
}

pub fn pp_args(args: &[ArgType]) -> RcDoc {
pub fn pp_args(args: &[IDLArgType]) -> RcDoc {
let args = args.iter().map(|arg| {
if let Some(name) = &arg.name {
pp_text(name).append(kwd(" :")).append(pp_ty(&arg.typ))
Expand All @@ -171,7 +171,7 @@ pub fn pp_args(args: &[ArgType]) -> RcDoc {
enclose("(", doc, ")")
}

pub fn pp_rets(rets: &[Type]) -> RcDoc {
pub fn pp_rets(rets: &[IDLType]) -> RcDoc {
let doc = concat(rets.iter().map(pp_ty), ",");
enclose("(", doc, ")")
}
Expand All @@ -187,12 +187,12 @@ pub fn pp_modes(modes: &[FuncMode]) -> RcDoc {
RcDoc::concat(modes.iter().map(|m| RcDoc::space().append(pp_mode(m))))
}

fn pp_service(serv: &[(String, Type)]) -> RcDoc {
fn pp_service(serv: &[Binding]) -> RcDoc {
let doc = concat(
serv.iter().map(|(id, func)| {
let func_doc = match func.as_ref() {
TypeInner::Func(ref f) => pp_function(f),
TypeInner::Var(_) => pp_ty(func),
serv.iter().map(|Binding { id, typ }| {
let func_doc = match typ {
IDLType::FuncT(ref f) => pp_function(f),
IDLType::VarT(_) => pp_ty(typ),
_ => unreachable!(),
};
pp_text(id).append(kwd(" :")).append(func_doc)
Expand All @@ -202,29 +202,29 @@ fn pp_service(serv: &[(String, Type)]) -> RcDoc {
enclose_space("{", doc, "}")
}

fn pp_defs(env: &TypeEnv) -> RcDoc {
lines(env.0.iter().map(|(id, ty)| {
fn pp_defs(env: &IDLMergedProg) -> RcDoc {
lines(env.get_types().iter().map(|(id, typ)| {
kwd("type")
.append(ident(id))
.append(kwd("="))
.append(pp_ty(ty))
.append(pp_ty(typ))
.append(";")
}))
}

fn pp_actor(ty: &Type) -> RcDoc {
match ty.as_ref() {
TypeInner::Service(ref serv) => pp_service(serv),
TypeInner::Var(_) | TypeInner::Class(_, _) => pp_ty(ty),
fn pp_actor(ty: &IDLType) -> RcDoc {
match ty {
IDLType::ServT(ref serv) => pp_service(serv),
IDLType::VarT(_) | IDLType::ClassT(_, _) => pp_ty(ty),
_ => unreachable!(),
}
}

pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [ArgType]) -> RcDoc<'a> {
pub fn pp_init_args<'a>(env: &'a IDLMergedProg, args: &'a [IDLArgType]) -> RcDoc<'a> {
pp_defs(env).append(pp_args(args))
}
pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
match actor {
pub fn compile(env: &IDLMergedProg) -> String {
match &env.actor {
None => pp_defs(env).pretty(LINE_WIDTH).to_string(),
Some(actor) => {
let defs = pp_defs(env);
Expand Down
32 changes: 28 additions & 4 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::CandidType;
use crate::idl_hash;
use crate::types::syntax::IDLType;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -177,6 +178,12 @@ impl TypeContainer {
}
.into()
}

pub fn as_idl_type(&self, ty: &Type) -> IDLType {
// The knot type has already been converted to a var type when adding the type to the env,
// see `self.add` and `self.go`
self.env.as_idl_type(ty)
}
}

#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
Expand Down Expand Up @@ -325,13 +332,24 @@ impl Type {
#[cfg(feature = "printer")]
impl fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", crate::pretty::candid::pp_ty(self).pretty(80))
write!(f, "{}", self.as_ref())
}
}
#[cfg(feature = "printer")]
impl fmt::Display for TypeInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", crate::pretty::candid::pp_ty_inner(self).pretty(80))
match self {
TypeInner::Future => write!(f, "future"),
TypeInner::Unknown => write!(f, "unknown"),
_ => {
let env = crate::TypeEnv::new();
write!(
f,
"{}",
crate::pretty::candid::pp_ty(&env.inner_as_idl_type(self)).pretty(80),
)
}
}
}
}
#[cfg(not(feature = "printer"))]
Expand Down Expand Up @@ -480,10 +498,11 @@ pub struct Field {
#[cfg(feature = "printer")]
impl fmt::Display for Field {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let env = crate::TypeEnv::new();
write!(
f,
"{}",
crate::pretty::candid::pp_field(self, false).pretty(80)
crate::pretty::candid::pp_field(&env.field_to_idl_field(self), false).pretty(80)
)
}
}
Expand Down Expand Up @@ -557,7 +576,12 @@ pub struct ArgType {
#[cfg(feature = "printer")]
impl fmt::Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", crate::pretty::candid::pp_function(self).pretty(80))
let env = crate::TypeEnv::new();
write!(
f,
"{}",
crate::pretty::candid::pp_function(&env.func_to_idl_func(self)).pretty(80),
)
}
}
#[cfg(not(feature = "printer"))]
Expand Down
10 changes: 6 additions & 4 deletions rust/candid/src/types/subtype.rs
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not change this code, because if we incur in Future or Unknown, this explodes now.

Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ pub fn equal(gamma: &mut Gamma, env: &TypeEnv, t1: &Type, t2: &Type) -> Result<(
let init_2 = to_tuple(&init2_typ);
equal(gamma, env, &init_1, &init_2).context(format!(
"Mismatch in init args: {} and {}",
pp_args(init1),
pp_args(init2)
pp_args(env, init1),
pp_args(env, init2)
))?;
equal(gamma, env, ty1, ty2)
}
Expand Down Expand Up @@ -316,7 +316,9 @@ fn pp_args(args: &[crate::types::ArgType]) -> String {
s
}
#[cfg(feature = "printer")]
fn pp_args(args: &[crate::types::ArgType]) -> String {
fn pp_args(env: &TypeEnv, args: &[crate::types::ArgType]) -> String {
use crate::pretty::candid::pp_args;
pp_args(args).pretty(80).to_string()
pp_args(&env.arg_types_to_idl_arg_types(args))
.pretty(80)
.to_string()
}
Loading