Skip to content

Some more tests related to subtyping#126

Merged
nomeata merged 35 commits intomasterfrom
joachim/test-new-subtyping
Nov 21, 2020
Merged

Some more tests related to subtyping#126
nomeata merged 35 commits intomasterfrom
joachim/test-new-subtyping

Conversation

@nomeata
Copy link
Copy Markdown
Contributor

@nomeata nomeata commented Oct 30, 2020

this adds some tests to account for the new behaviour of
https://github.com/dfinity/candid/pull/110f

Just a first stab, untested, and kinda running out of stream.

this adds some tests to account for the new behaviour of
https://github.com/dfinity/candid/pull/110f

Just a first stab, untested, and kinda running out of stream.
@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Oct 30, 2020

I am getting

Checking 61 opt: parsing null : null...thread 'decode_test_test_construct_test_did' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("type mismatch: null cannot be of type opt empty")', rust/candid/tests/test_suite.rs:11:16

which sounds like a lie (sure null can be of type opt t).

Also, it might be helpful to not number the tests squentially, but use the line number to identify them, makes it easier to to jump to them. This is what the test driver in Motoko is doing.

@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Oct 30, 2020

I could fix this with

--- a/rust/candid/src/parser/value.rs
+++ b/rust/candid/src/parser/value.rs
@@ -154,7 +154,7 @@ 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::Null, Type::Opt(_)) => 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>()?),

but then it seems the distinction between IDLValue::Null and IDLValue::None may not be needed. It is not clear to me why we’d need that – in the algebra of untyped values, they are the same, aren’t they?

Now at

Checking 67 opt: parsing opt true : opt bool...thread 'decode_test_test_construct_test_did' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("type mismatch: false cannot be of type opt bool")', rust/candid/tests/test_suite.rs:11:16

which means that the rule

not (null <: <datatype>)
<datatype> <: <datatype'>
-----------------------------
<datatype> <: opt <datatype'>

(which I find dubious, non-compositional, and wouldn’t be surprised if it breaks stuff) is not implemented. Will leave this to @chenyan-dfinity at this point.

@chenyan-dfinity
Copy link
Copy Markdown
Contributor

it seems the distinction between IDLValue::Null and IDLValue::None may not be needed.

In the untype setting, we need IDLValue::Null to represent null of type null and IDLValue::None to represent null of type opt T. The parser always parses null to be IDLValue::Null, and we need the if from_parser guard to ensure we don't change between null and none after the first type annotation.

The whole set of opt subtyping rules are not implemented. I will take a look to see the right place to add these.

Comment thread test/construct.test.did
Comment thread test/construct.test.did Outdated
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";

// special opt and record subtyping
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also some tests for opt variant?

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.

yes, didn't get to variant yes :-)

@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Nov 2, 2020

it seems the distinction between IDLValue::Null and IDLValue::None may not be needed.

In the untype setting, we need IDLValue::Null to represent null of type null and IDLValue::None to represent null of type opt T. The parser always parses null to be IDLValue::Null, and we need the if from_parser guard to ensure we don't change between null and none after the first type annotation.

Right, but why do you even have to distinguish the two? It’s not that IDLValue::None tells you the type of it (you still don’t know T). What breaks if you only use IDLValue::Null everywhere?

nomeata added a commit that referenced this pull request Nov 2, 2020
This is a significant rewirte of the section in the spec describing
deserialization. Some points worth noting:

 * This replaces the elaboration relation with one that takes a value
   and a type as input, and returns a value. This makes it clearer that
   there is no “input _type_” of deeper relevance.

 * One great benefit: No more long prose about how the rules assume
   these types to be principal, even if they aren’t.

   This clarifies questions like [this
   one](#126 (comment))

 * Also, the existing rules about “elaborating function values” were
   kinda bogus, as function values are just accepted as they are. This
   is much easier now, also good.

 * Unfortuantely for this application, our textual representation has
   overloading. Instead of defining yet another abstract value algebra,
   I did a bit of hand-waving, and defined an “overloading-free
   fragment” of the textual format for this section.

 * A fair number of rules becomes simpler. Promising!

 * I was able to phrase some interesting properties more formally. Also
   promising! Didn’t actually prove them, though, althogh we should.

 * Still unclear to me what we mean with “subtyping is complete”, see
   the section there.

 * Decoding is still an inductive relation, so not fully algorithmic.
   This may be an issue with a stright-forward formalization; not all
   theorem provers like negative occurrences of inductive relations in
   their rules.

 * Syntax of the relations up for discussion.
@chenyan-dfinity
Copy link
Copy Markdown
Contributor

Right, but why do you even have to distinguish the two? It’s not that IDLValue::None tells you the type of it (you still don’t know T). What breaks if you only use IDLValue::Null everywhere?

Ah right. It was a workaround when we don't have type annotations. Yes, this is not necessary now.

@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Nov 3, 2020

Ah right. It was a workaround when we don't have type annotations. Yes, this is not necessary now.

Gave it a shot, but still needed internally in the current code structure, because idl_serialize only takes an IDLValue, but not the type, so when serializing null it is unclear which form it should take. This is a bit fishy, and is also the reason why the IDLValue takes this idx that doesn’t really make sense for the generic IDLValue type.

The Spec solves this by making the M function indexed by the type to encode at, but I don’t feel like refactoring the rust code now :-)

@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Nov 3, 2020

Just added a few nat <: int subtyping tests, and tried to add the subtyping.

It’s a bit hard that the test suite will panc (rather than report an error and continue) upon bad subtyping. The unwrap in HostTest.from_assert is causing problems.

@nomeata nomeata marked this pull request as ready for review November 3, 2020 11:08
@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Nov 3, 2020

The rust code chanes might need some cleanup, feel free to push to this branch.

nomeata added a commit that referenced this pull request Nov 4, 2020
This is a significant rewirte of the section in the spec describing
deserialization. Some points worth noting:

 * This replaces the elaboration relation with one that takes a value
   and a type as input, and returns a value. This makes it clearer that
   there is no “input _type_” of deeper relevance.

 * One great benefit: No more long prose about how the rules assume
   these types to be principal, even if they aren’t.

   This clarifies questions like [this
   one](#126 (comment))

 * Also, the existing rules about “elaborating function values” were
   kinda bogus, as function values are just accepted as they are. This
   is much easier now, also good.

 * Unfortuantely for this application, our textual representation has
   overloading. Instead of defining yet another abstract value algebra,
   I did a bit of hand-waving, and defined an “overloading-free
   fragment” of the textual format for this section.

 * A fair number of rules becomes simpler. Promising!

 * I was able to phrase some interesting properties more formally. Also
   promising! Didn’t actually prove them, though, although we should.

 * Still unclear to me what we mean with “subtyping is complete”, see
   the section there.

 * Decoding is still an inductive relation, so not fully algorithmic.
   This may be an issue with a stright-forward formalization; not all
   theorem provers like negative occurrences of inductive relations in
   their rules.
nomeata added a commit to caffeinelabs/motoko that referenced this pull request Nov 18, 2020
this propagates coercion errors (via a special sentinel value), and
recovers when parsing opt. It also implements
subtyping-to-consitutient-type.

The updated Candid test suite from
dfinity/candid#126 passes, but I am not
overly confident in it. More thorough randomized testing would be
needed.
@nomeata
Copy link
Copy Markdown
Contributor Author

nomeata commented Nov 20, 2020

Thanks for looking at the code. Feel free to take over and change the rust code in whatever way you see fit.

Copy link
Copy Markdown
Contributor

@chenyan-dfinity chenyan-dfinity left a comment

Choose a reason for hiding this comment

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

LGTM

@nomeata nomeata merged commit f6f794f into master Nov 21, 2020
@nomeata nomeata deleted the joachim/test-new-subtyping branch November 21, 2020 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants