Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
57bdd3b
Some more tests related to subtyping
nomeata Oct 30, 2020
671c80f
I guess this one is good now
nomeata Oct 30, 2020
92fa86c
Allow null : opt t subtyping?
nomeata Oct 30, 2020
23520f6
Test case: add `nat <: int` tests
nomeata Nov 3, 2020
c666291
nat/int subtyping
nomeata Nov 3, 2020
82b71f4
Can the test suite be more liberal?
nomeata Nov 3, 2020
0b3e1a9
stash
nomeata Nov 3, 2020
a4a3af9
Merge branch 'master' of github.com:dfinity/candid into joachim/test-…
nomeata Nov 3, 2020
688b236
Undo changes to test suite runnter
nomeata Nov 3, 2020
2173a51
comment
nomeata Nov 3, 2020
7196f9b
reserved <: opt t
nomeata Nov 3, 2020
66624c2
Fix test cases (forgot about a-normal-form)
nomeata Nov 3, 2020
4cb1d32
Fix more test cases
nomeata Nov 3, 2020
47d5fc5
Optional record fiels are optional
nomeata Nov 3, 2020
429ea4a
Reserved fields may be missing
nomeata Nov 3, 2020
236638f
Merge branch 'master' into joachim/test-new-subtyping
nomeata Nov 13, 2020
b6a246e
Run cargo-fmt
nomeata Nov 13, 2020
e96249c
Test case for reserved <: opt nat
nomeata Nov 13, 2020
7aae87f
Add `true : fix opt` test
nomeata Nov 17, 2020
3924e4c
Update tests for opt-via-consituent type rules
nomeata Nov 17, 2020
4634331
cargo-fmt
nomeata Nov 17, 2020
55f0e8c
More tests about variants
nomeata Nov 17, 2020
eb1547d
fix maxint tests
nomeata Nov 18, 2020
f037f1d
Fun test case with `fix record <: fix (opt . record)`
nomeata Nov 19, 2020
dc5deec
It shouldn’t actually decode
nomeata Nov 19, 2020
007e7bd
Run cargo fix
nomeata Nov 19, 2020
e6492ab
Fix cargo clippy
nomeata Nov 19, 2020
52fa2bd
Merge branch 'master' into joachim/test-new-subtyping
nomeata Nov 19, 2020
5e2813a
More clippy
nomeata Nov 19, 2020
1d841b9
refactor
chenyan-dfinity Nov 20, 2020
a5a7f5a
More tests (this one showed a bug in Motoko)
nomeata Nov 20, 2020
a259759
Fix escaping of escapes
nomeata Nov 20, 2020
5337a14
fix
chenyan-dfinity Nov 20, 2020
4038a19
Merge branch 'joachim/test-new-subtyping' of github.com:dfinity/candi…
chenyan-dfinity Nov 20, 2020
12a7795
Merge branch 'master' into joachim/test-new-subtyping
nomeata Nov 21, 2020
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
2 changes: 1 addition & 1 deletion rust/candid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ macro_rules! Encode {
Encode!(@PutValue builder $($x,)*)
}};
( @PutValue $builder:ident $x:expr, $($tail:expr,)* ) => {{
$builder.arg($x).and_then(|mut builder| Encode!(@PutValue builder $($tail,)*))
$builder.arg($x).and_then(|builder| Encode!(@PutValue builder $($tail,)*))
}};
( @PutValue $builder:ident ) => {{
$builder.serialize_to_vec()
Expand Down
7 changes: 7 additions & 0 deletions rust/candid/src/parser/typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ impl TypeEnv {
t => Ok(t),
}
}
pub fn trace_type<'a>(&'a self, t: &'a Type) -> Result<Type> {
match t {
Type::Var(id) => self.trace_type(self.find_type(id)?),
Type::Knot(ref id) => self.trace_type(&crate::types::internal::find_type(id).unwrap()),
t => Ok(t.clone()),
}
}
pub fn as_func<'a>(&'a self, t: &'a Type) -> Result<&'a Function> {
match t {
Type::Func(f) => Ok(f),
Expand Down
30 changes: 24 additions & 6 deletions rust/candid/src/parser/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ impl IDLValue {
let ty = crate::types::internal::find_type(id).unwrap();
self.annotate_type(from_parser, env, &ty)?
}
(IDLValue::Null, Type::Opt(_)) if from_parser => IDLValue::None,
(IDLValue::Float64(n), Type::Float32) if from_parser => IDLValue::Float32(*n as f32),
(IDLValue::Number(str), t) if from_parser => match t {
Type::Int => IDLValue::Int(str.parse::<Int>()?),
Expand All @@ -177,8 +176,9 @@ impl IDLValue {
(_, Type::Reserved) => IDLValue::Reserved,
(IDLValue::Null, Type::Null) => IDLValue::Null,
(IDLValue::Bool(b), Type::Bool) => IDLValue::Bool(*b),
(IDLValue::Int(i), Type::Int) => IDLValue::Int(i.clone()),
(IDLValue::Nat(n), Type::Nat) => IDLValue::Nat(n.clone()),
(IDLValue::Int(i), Type::Int) => IDLValue::Int(i.clone()),
(IDLValue::Nat(n), Type::Int) => IDLValue::Int(n.clone().into()),
(IDLValue::Nat8(n), Type::Nat8) => IDLValue::Nat8(*n),
(IDLValue::Nat16(n), Type::Nat16) => IDLValue::Nat16(*n),
(IDLValue::Nat32(n), Type::Nat32) => IDLValue::Nat32(*n),
Expand All @@ -190,11 +190,23 @@ impl IDLValue {
(IDLValue::Float64(n), Type::Float64) => IDLValue::Float64(*n),
(IDLValue::Float32(n), Type::Float32) => IDLValue::Float32(*n),
(IDLValue::Text(s), Type::Text) => IDLValue::Text(s.to_owned()),
// opt parsing. NB: Always succeeds!
(IDLValue::Null, Type::Opt(_)) => IDLValue::None,
(IDLValue::Reserved, Type::Opt(_)) => IDLValue::None,
(IDLValue::None, Type::Opt(_)) => IDLValue::None,
(IDLValue::Opt(v), Type::Opt(ty)) => {
let v = v.annotate_type(from_parser, env, ty)?;
IDLValue::Opt(Box::new(v))
// liberal decoding of optionals
(IDLValue::Opt(v), Type::Opt(ty)) => v
.annotate_type(from_parser, env, ty)
.map(|v| IDLValue::Opt(Box::new(v)))
.unwrap_or(IDLValue::None),
// try consituent type
(v, Type::Opt(ty)) if !matches!(env.trace_type(ty)?, Type::Null|Type::Reserved|Type::Opt(_)) => {
v.annotate_type(from_parser, env, ty)
.map(|v| IDLValue::Opt(Box::new(v)))
.unwrap_or(IDLValue::None)
}
// fallback
(_, Type::Opt(_)) => IDLValue::None,
(IDLValue::Vec(vec), Type::Vec(ty)) => {
let mut res = Vec::new();
for e in vec.iter() {
Expand All @@ -210,7 +222,13 @@ impl IDLValue {
for Field { id, ty } in fs.iter() {
let val = fields
.get(&id)
.ok_or_else(|| Error::msg(format!("field {} not found", id)))?;
.cloned()
.or_else(|| match env.trace_type(ty).unwrap() {
Type::Opt(_) => Some(&IDLValue::None),
Type::Reserved => Some(&IDLValue::Reserved),
_ => None,
})
.ok_or_else(|| Error::msg(format!("required field {} not found", id)))?;
let val = val.annotate_type(from_parser, env, ty)?;
res.push(IDLField {
id: id.clone(),
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ thread_local! {
pub(crate) fn find_type(id: &TypeId) -> Option<Type> {
ENV.with(|e| match e.borrow().get(id) {
None => None,
Some(t) => Some((*t).clone()),
Some(t) => Some(t.clone()),
})
}

Expand Down
8 changes: 8 additions & 0 deletions rust/candid/src/types/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ impl From<BigUint> for Nat {
}
}

impl From<Nat> for Int {
#[inline(always)]
fn from(n: Nat) -> Self {
let i: BigInt = n.0.into();
i.into()
}
}

impl From<Int> for BigInt {
#[inline(always)]
fn from(i: Int) -> Self {
Expand Down
45 changes: 41 additions & 4 deletions test/construct.test.did
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Encoding tests for construct types
type Opt = opt Opt;
type Vec = vec Vec;
type EmptyRecord = record { EmptyRecord };
type OptRec = opt record { OptRec };

type List = opt record { head : int; tail : List };
type List1 = opt List2;
type List2 = record { head : int; tail : List1 };
Expand Down Expand Up @@ -47,7 +49,7 @@ assert blob "DIDL\02\6e\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (Opt) "op
// vector
assert blob "DIDL\01\6d\7c\01\00\00" == "(vec {})" : (vec int) "vec: empty";
assert blob "DIDL\01\6d\7c\01\00\02\01\02" == "(vec { 1; 2 })" : (vec int) "vec";
assert blob "DIDL\01\6d\7b\01\00\02\01\02" == "(blob \"\01\02\")" : (vec nat8) "vec: blob";
assert blob "DIDL\01\6d\7b\01\00\02\01\02" == "(blob \"\\01\\02\")" : (vec nat8) "vec: blob";
assert blob "DIDL\01\6d\00\01\00\00" == "(vec {})" : (Vec) "vec: recursive vector";
assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (Vec) "vec: tree";
assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (vec vec empty) "vec: non-recursive tree";
Expand Down Expand Up @@ -85,14 +87,47 @@ assert blob "DIDL\01\6c\02\00\7c\01\7e\02\7e\01\00\2a\01\00" !
assert blob "DIDL\01\6c\02\00\01\01\7e\01\00\00\00" !: (record {}) "record: type out of range";
assert blob "DIDL\01\6c\02\00\01\01\7e\01\7c\2a" !: (reserved) "record: type out of range";

// opt
assert blob "DIDL\00\01\7f" == "(null)" : (opt empty) "opt: parsing null : null";
assert blob "DIDL\01\6e\6f\01\00\00" == "(null)" : (opt empty) "opt: parsing null : opt empty";
assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt bool) "opt: parsing null : opt bool ";
assert blob "DIDL\01\6e\7e\01\00\01\00" == "(opt false)" : (opt bool) "opt: parsing opt false : opt bool";
assert blob "DIDL\01\6e\7e\01\00\01\01" == "(opt true)" : (opt bool) "opt: parsing opt true : opt bool";
assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt bool) "opt: parsing invalid bool at opt bool";

// nested opt
assert blob "DIDL\02\6e\01\6e\7e\01\00\00" == "(null)" : (opt opt bool) "opt: parsing null : opt opt bool";
assert blob "DIDL\02\6e\01\6e\7e\01\00\01\00" == "(opt null)" : (opt opt bool) "opt: parsing opt null : opt opt bool";
assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\00" == "(opt opt false)" : (opt opt bool) "opt: parsing opt opt false : opt opt bool";

// opt subtype to constituent
assert blob "DIDL\00\01\7e\00" == "(opt false)" : (opt bool) "opt: parsing false : opt bool";
assert blob "DIDL\00\01\7e\01" == "(opt true)" : (opt bool) "opt: parsing true : opt bool";
assert blob "DIDL\00\01\7e\02" !: (opt bool) "opt: parsing invalid bool at opt bool";
assert blob "DIDL\01\6e\7f\01\00\00" == "(null)" : (opt opt null) "opt: parsing (null : opt null) at opt opt null gives null, not opt null";
assert blob "DIDL\00\01\7e\01" == "(null)" : (opt opt bool) "opt: parsing true : opt opt bool gives null";
assert blob "DIDL\00\01\7e\01" == "(null)" : (Opt) "opt: parsing (true : bool) at \"fix opt\" gives null";
assert blob "DIDL\01\6c\01\00\00\01\00" !: (OptRec) "opt: parsing \"fix record\" at \"fix record opt\" fails";

// special opt subtyping
assert blob "DIDL\00\01\70" == "(null)" : (opt nat) "reserved <: opt nat";
assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt nat) "null : opt bool <: opt nat";
assert blob "DIDL\01\6e\7e\01\00\01\01" == "(null)" : (opt nat) "opt true : opt bool <: opt nat";
assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt nat) "opt bool <: opt nat with invalid boolean value";
assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(null)" : (opt nat) "opt opt true : opt opt bool <: opt nat";
assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(opt null)" : (opt opt nat) "opt opt true : opt opt bool <: opt opt nat";
assert blob "DIDL\02\6e\01\6b\01\00\7e\01\00\01\00\01" == "(null)" : (opt variant { 0 : int }) "opt: recovered coercion error under variant";

// special record field rules
assert blob "DIDL\01\6c\00\01\00" == "(record { foo = null })" : (record { foo : opt bool }) "missing optional field";
assert blob "DIDL\01\6c\00\01\00" == "(record { foo = \"☃\" })" : (record { foo : reserved }) "missing reserved field";

// variant
assert "(variant {})" !: (variant {}) "variant: no empty value";
assert blob "DIDL\01\6b\00\01\00" !: (variant {}) "variant: no empty value";
assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0}) "variant: numbered field";
assert blob "DIDL\01\6b\01\00\7f\01\00\00\2a" !: (variant {0:int}) "variant: type mismatch";

// TODO Should this pass?
assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0:int; 1:int}) "??? variant: type mismatch";
assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0:int; 1:int}) "variant: type mismatch in unused tag";
Comment thread
chenyan-dfinity marked this conversation as resolved.
assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0;1}) "variant: ignore field";
assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\00" == "(variant {0})" : (variant {0}) "variant: ignore field";
assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\01" == "(variant {1})" : (variant {1;2}) "variant: change index";
Expand All @@ -101,6 +136,8 @@ assert blob "DIDL\01\6b\01\00\7f\01\00\01" !
assert blob "DIDL\01\6b\02\00\7f\00\7f\01\00\00" !: (variant {0}) "variant: duplicate fields";
assert blob "DIDL\01\6b\03\00\7f\01\7f\01\7f\01\00\00" !: (variant {0}) "variant: duplicate fields";
assert blob "DIDL\01\6b\02\01\7f\00\7f\01\00\00" !: (variant {0;1}) "variant: unsorted";
assert blob "DIDL\01\6b\03\00\7f\01\7f\ff\ff\ff\ff\0f\7f\01\00\00" : (variant {0;1;4294967295}) "variant: ok with maxInt";
assert blob "DIDL\01\6b\03\00\7f\ff\ff\ff\ff\0f\7f\01\7f\01\00\00" !: (variant {0;1;4294967295}) "variant: unsorted with maxInt";
assert blob "DIDL\01\6b\02\b3\d3\c9\01\7f\e6\fd\d5\01\7f\01\00\00" == "(variant { Bar })" : (variant { Foo; Bar; }) "variant: enum";
assert blob "DIDL\01\6b\01\cd\84\b0\05\7f\01\00\00" == "(variant { \"☃\" })" : (variant { "☃" }) "variant: unicode field";
assert blob "DIDL\01\6b\02\bc\8a\01\71\c5\fe\d2\01\71\01\00\00\04\67\6f\6f\64"
Expand Down
6 changes: 6 additions & 0 deletions test/prim.test.did
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ assert blob "DIDL\00\01\7c\80\7f" == "(-128)" : (int) "int: leb not overlong w
assert blob "DIDL\00\01\7c\80\80\98\f4\e9\b5\ca\ea\00" == "(60000000000000000)" : (int) "int: big number";
assert blob "DIDL\00\01\7c\80\80\e8\8b\96\ca\b5\95\7f" == "(-60000000000000000)" : (int) "int: negative big number";

assert blob "DIDL\00\01\7d\00" == "(0)" : (int) "nat <: int: 0";
assert blob "DIDL\00\01\7d\01" == "(1)" : (int) "nat <: int: 1";
assert blob "DIDL\00\01\7d\7f" == "(127)" : (int) "nat <: int: leb (two bytes)";
assert blob "DIDL\00\01\7d\80\01" == "(128)" : (int) "nat <: int: leb (two bytes)";
assert blob "DIDL\00\01\7d\ff\7f" == "(16383)" : (int) "nat <: int: leb (two bytes, all bits)";

assert blob "DIDL\00\01\7b\00" == "(0)" : (nat8) "nat8: 0";
assert blob "DIDL\00\01\7b\01" == "(1)" : (nat8) "nat8: 1";
assert blob "DIDL\00\01\7b\ff" == "(255)" : (nat8) "nat8: 255";
Expand Down