From 8c67be55ce39ab1d82198bdac92b0af5e601bbac Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 10 May 2024 13:39:52 +0200 Subject: [PATCH] Fixes #921: top-level Miller-loop needs not to be serialisable This is a bit tricky, but in a similar way where we allow functions to be returned by functions, this must also work for MillerLoopResult. --- CHANGELOG.md | 31 +++++++++++++----- crates/aiken-lang/src/tests/check.rs | 47 ++++++++++++++++++++++++++++ crates/aiken-lang/src/tipo/expr.rs | 13 ++++---- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c485fd..8806794e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,9 @@ ### Added +- **aiken**: added export command that exporting of regular function definitons. @rvcas - **aiken-lsp**: hover and goto definition support on list tail. @rvcas - **aiken-lsp**: hover on prop test via expression. @rvcas -- **aiken**: added export command that exporting of regular function definitons. @rvcas - **aiken-lang**: a new way to emit logs that don't get erased. @micahkendall ### Fixed @@ -14,14 +14,29 @@ - **aiken-lang**: formatter should not erase `pub` on validators. @rvcas - **aiken-lang**: error on using tuple index when a tuple is returned by a generic function. @rvcas - **aiken-lang**: fix a regression in the Type-checker introduced in v1.0.25-alpha regarding types comparison. See #917. @KtorZ -- **aiken-lang**: Fix incongruous generics after type-checking which caused [] to be treated as a list in cases where it needed to be an empty map primitive. See #922. @KtorZ -- **aiken-lang**: Fix for generic constrs being used as functions causing type mismatch errors. @Microproofs -- **aiken-lang**: Fix for error occuring when a field holds Data that is not a constr type when compiler traces are on. @Microproofs +- **aiken-lang**: fix incongruous generics after type-checking which caused [] to be treated as a list in cases where it needed to be an empty map primitive. See #922. @KtorZ +- **aiken-lang**: fix for generic constrs being used as functions causing type mismatch errors. @Microproofs +- **aiken-lang**: fix for error occuring when a field holds Data that is not a constr type when compiler traces are on. @Microproofs +- **aiken-lang**: fix compiler wrongly requiring MillerLoopResult to be 'serialisable' when manipulated as a top-level value. @KtorZ -### Changed -- **aiken-lang**: **MAJOR CHANGE** 2-tuples are now treated the same as 3+ tuples. To replace the representation of pairs at the uplc level, we now have a new Prelude type called Pair with 2 generic arguments. The main place you will see its usage is in the script context. For existing contracts you can continue to use 2-tuples, just note the offchain representation is an array of 2 items in CBOR. @KtorZ @Microproofs -- **aiken-lang**: Some more code gen cleanup. @Microproofs -- **aiken-lang**: New optimization for wrapped builtins found in the stdlib. @Microproofs +### Changed + +> [!WARNING] +> +> **BREAKING-CHANGE** +> +> 2-tuples `(a, b)` are now treated the same as 3+ tuples -- which directly impacts the way that Aiken now deserialise those, especially when nested inside a `List`. +> +> To deserialize into a list of 2-tuple (`List<(a, b)>`), one is now expected to provide a CBOR array of arrays (of 2 elements). Previously, this would require to provide a CBOR map! The downside of the latter is that CBOR serialization libraries do not necessarily preserve the order of keys in a map which could cause issues down the line, in particular with Aiken's dictionnaries. +> +> +> To recover the old behavior when desired, Aiken introduces a new type `Pair` to the language. So any existing program can be migrated by switching any occurences of `(a, b)` to `Pair`. +> +> However, it is often preferable to use 2-tuples where possible. The main place you will see usage of `Pair` is in the script context because its form is imposed by the ledger. + +- **aiken-lang**: altered internal representation of 2-tuples to distinguish them from pairs. @KtorZ @Microproofs +- **aiken-lang**: some more code gen cleanup. @Microproofs +- **aiken-lang**: new optimization for wrapped builtins found in the stdlib. @Microproofs ## v1.0.26-alpha - 2024-03-25 diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index bfdadd58..aae85d07 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -207,6 +207,53 @@ fn illegal_inhabitants_returned() { )) } +#[test] +fn not_illegal_top_level_unserialisable() { + let source_code = r#" + fn foo() -> MillerLoopResult { + todo + } + "#; + + assert!(check(parse(source_code)).is_ok()); +} + +#[test] +fn illegal_unserialisable_in_generic_fn() { + let source_code = r#" + type Foo { + foo: a + } + + fn main() -> Foo Bool> { + todo + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::IllegalTypeInData { .. })) + )) +} + +#[test] +fn illegal_unserialisable_in_generic_miller_loop() { + let source_code = r#" + type Foo { + foo: a + } + + fn main() -> Foo { + todo + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::IllegalTypeInData { .. })) + )) +} + #[test] fn mark_constructors_as_used_via_field_access() { let source_code = r#" diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index c6b4a634..05f27aa7 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -2563,7 +2563,7 @@ fn assert_assignment(expr: TypedExpr) -> Result { Ok(expr) } -pub fn ensure_serialisable(allow_fn: bool, t: Rc, location: Span) -> Result<(), Error> { +pub fn ensure_serialisable(is_top_level: bool, t: Rc, location: Span) -> Result<(), Error> { match t.deref() { Type::App { args, @@ -2573,7 +2573,7 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc, location: Span) -> Resul contains_opaque: _, alias: _, } => { - if t.is_ml_result() { + if !is_top_level && t.is_ml_result() { return Err(Error::IllegalTypeInData { tipo: t.clone(), location, @@ -2601,7 +2601,7 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc, location: Span) -> Resul ret, alias: _, } => { - if !allow_fn { + if !is_top_level { return Err(Error::IllegalTypeInData { tipo: t.clone(), location, @@ -2609,21 +2609,22 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc, location: Span) -> Resul } args.iter() - .map(|e| ensure_serialisable(allow_fn, e.clone(), location)) + .map(|e| ensure_serialisable(true, e.clone(), location)) .collect::, _>>()?; - ensure_serialisable(allow_fn, ret.clone(), location) + ensure_serialisable(true, ret.clone(), location) } Type::Var { tipo, alias } => match tipo.borrow().deref() { TypeVar::Unbound { .. } => Ok(()), TypeVar::Generic { .. } => Ok(()), TypeVar::Link { tipo } => ensure_serialisable( - allow_fn, + is_top_level, Type::with_alias(tipo.clone(), alias.clone()), location, ), }, + Type::Pair { fst, snd, .. } => { ensure_serialisable(false, fst.clone(), location)?; ensure_serialisable(false, snd.clone(), location)