Skip to content

Commit 7e59c47

Browse files
committed
Merge BlockstreamResearch#65: Add type casts
a3bb49d test: Use casts in example programs (Christian Lewe) e2f1920 feat: Cast values of structurally equivalent types (Christian Lewe) Pull request description: Depends on BlockstreamResearch#64 Add type casts based on structural type equality. ACKs for top commit: apoelstra: ACK a3bb49d short and sweet Tree-SHA512: f1381e6faa941b682e03ff05ed826b6768f119b02851276952772b11901824f763160f7b8b8b14d108e4221a5556a943def55b067cdce84d86246569941898a5
2 parents ccac596 + a3bb49d commit 7e59c47

File tree

8 files changed

+48
-13
lines changed

8 files changed

+48
-13
lines changed

example_progs/cat.simf

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
fn main() {
2-
let a: u8 = 0x10;
3-
let b: u8 = 0x01;
4-
let ab: u16 = (a, b);
2+
let ab: u16 = <(u8, u8)>::into((0x10, 0x01));
53
let c: u16 = 0x1001;
64
jet_verify(jet_eq_16(ab, c));
7-
let a: u4 = 0b1011;
8-
let b: u4 = 0b1101;
9-
let ab: u8 = (a, b);
5+
let ab: u8 = <(u4, u4)>::into((0b1011, 0b1101));
106
let c: u8 = 0b10111101;
117
jet_verify(jet_eq_8(ab, c));
128
}

example_progs/function.simf

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
fn checked_add_32(a: u32, b: u32) -> u32 {
22
let (carry, sum): (bool, u32) = jet_add_32(a, b);
3-
// FIXME: Need to cast `bool` to `u1`
4-
// jet_verify(jet_complement_1(carry));
3+
jet_verify(<u1>::into(jet_complement_1(<bool>::into(carry))));
54
sum
65
}
76

src/ast.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use crate::error::{Error, RichError, WithSpan};
1010
use crate::parse;
1111
use crate::parse::{FunctionName, Identifier, MatchPattern, Span, WitnessName};
1212
use crate::pattern::Pattern;
13-
use crate::types::{AliasedType, ResolvedType, TypeConstructible, TypeDeconstructible};
13+
use crate::types::{
14+
AliasedType, ResolvedType, StructuralType, TypeConstructible, TypeDeconstructible,
15+
};
1416
use crate::value::{UIntValue, Value};
1517

1618
/// Map of witness names to their expected type, as declared in the program.
@@ -233,6 +235,8 @@ pub enum CallName {
233235
UnwrapRight(ResolvedType),
234236
/// [`Option::unwrap`].
235237
Unwrap,
238+
/// Cast from the given source type.
239+
TypeCast(ResolvedType),
236240
/// A custom function that was defined previously.
237241
///
238242
/// We effectively copy the function body into every call of the function.
@@ -899,6 +903,20 @@ impl AbstractSyntaxTree for Call {
899903
scope,
900904
)?])
901905
}
906+
CallName::TypeCast(source) => {
907+
if from.args.len() != 1 {
908+
return Err(Error::InvalidNumberOfArguments(1, from.args.len()))
909+
.with_span(from);
910+
}
911+
if StructuralType::from(&source) != StructuralType::from(ty) {
912+
return Err(Error::InvalidCast(source, ty.clone())).with_span(from);
913+
}
914+
Arc::from([Expression::analyze(
915+
from.args.first().unwrap(),
916+
&source,
917+
scope,
918+
)?])
919+
}
902920
CallName::Custom(function) => {
903921
if from.args.len() != function.params().len() {
904922
return Err(Error::InvalidNumberOfArguments(
@@ -951,6 +969,9 @@ impl AbstractSyntaxTree for CallName {
951969
.map(Self::UnwrapRight)
952970
.with_span(from),
953971
parse::CallName::Unwrap => Ok(Self::Unwrap),
972+
parse::CallName::TypeCast(target) => {
973+
scope.resolve(target).map(Self::TypeCast).with_span(from)
974+
}
954975
parse::CallName::Custom(name) => scope
955976
.get_function(name)
956977
.cloned()

src/compile.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,13 @@ impl Call {
301301
let get_inner = ProgNode::assertr_take(fail_cmr, &ProgNode::iden());
302302
ProgNode::comp(&right_and_unit, &get_inner).with_span(self)
303303
}
304+
CallName::TypeCast(..) => {
305+
// A cast converts between two structurally equal types.
306+
// Structural equality of Simfony types A and B means
307+
// exact equality of the underlying Simplicity types of A and of B.
308+
// Therefore, a Simfony cast is a NOP in Simplicity.
309+
Ok(args)
310+
}
304311
CallName::Custom(function) => {
305312
let params_pattern = Pattern::tuple(
306313
function

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ pub enum Error {
154154
CannotCompile(String),
155155
JetDoesNotExist(JetName),
156156
TypeValueMismatch(ResolvedType),
157+
InvalidCast(ResolvedType, ResolvedType),
157158
MainNoInputs,
158159
MainNoOutput,
159160
MainRequired,
@@ -212,6 +213,10 @@ impl fmt::Display for Error {
212213
f,
213214
"Value does not match the assigned type `{ty}`"
214215
),
216+
Error::InvalidCast(source, target) => write!(
217+
f,
218+
"Cannot cast values of type `{source}` as values of type `{target}`"
219+
),
215220
Error::MainNoInputs => write!(
216221
f,
217222
"Main function takes no input parameters"

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ mod tests {
6666
("add.simf", "empty.wit"),
6767
("add.simf", "empty.wit"),
6868
("array.simf", "empty.wit"),
69-
// ("cat.simf", "empty.wit"),
69+
("cat.simf", "empty.wit"),
7070
(
7171
"checksigfromstackverify.simf",
7272
"checksigfromstackverify.wit",

src/minimal.pest

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ none_expr = @{ "None" }
5555
some_expr = { "Some(" ~ expression ~ ")" }
5656
false_expr = @{ "false" }
5757
true_expr = @{ "true" }
58-
unwrap_left = ${ "unwrap_left::<" ~ ty ~ ">" }
59-
unwrap_right = ${ "unwrap_right::<" ~ ty ~ ">" }
58+
unwrap_left = { "unwrap_left::<" ~ ty ~ ">" }
59+
unwrap_right = { "unwrap_right::<" ~ ty ~ ">" }
6060
unwrap = @{ "unwrap" }
61-
call_name = ${ jet | unwrap_left | unwrap_right | unwrap | function_name }
61+
type_cast = { "<" ~ ty ~ ">::into" }
62+
call_name = { jet | unwrap_left | unwrap_right | unwrap | type_cast | function_name }
6263
call_args = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
6364
call_expr = { call_name ~ call_args }
6465
unsigned_decimal = @{ (ASCII_DIGIT | "_")+ }

src/parse.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ pub enum CallName {
278278
UnwrapRight(AliasedType),
279279
/// Some unwrap function.
280280
Unwrap,
281+
/// Cast from the given source type.
282+
TypeCast(AliasedType),
281283
/// Name of a custom function.
282284
Custom(FunctionName),
283285
}
@@ -813,6 +815,10 @@ impl PestParse for CallName {
813815
AliasedType::parse(inner).map(Self::UnwrapRight)
814816
}
815817
Rule::unwrap => Ok(Self::Unwrap),
818+
Rule::type_cast => {
819+
let inner = pair.into_inner().next().unwrap();
820+
AliasedType::parse(inner).map(Self::TypeCast)
821+
}
816822
Rule::function_name => FunctionName::parse(pair).map(Self::Custom),
817823
_ => panic!("Corrupt grammar"),
818824
}

0 commit comments

Comments
 (0)