diff --git a/CHANGELOG.md b/CHANGELOG.md index 8806794e..26615d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,9 @@ - **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 +- **aiken-lang**: fix compiler wrongly requiring MillerLoopResult to be 'serialisable' when manipulated as a top-level value. See #921. @KtorZ +- **aiken-lang**: fix type-checker oversight regarding serialisation of generics. See #939. @KtorZ +- **aiken-lang**: fix type-checker not raising error when comparing non-serialisable types. See #940. @KtorZ ### Changed diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 274fabcd..273ff485 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -189,11 +189,11 @@ fn illegal_inhabitants_nested() { fn illegal_function_comparison() { let source_code = r#" fn not(x: Bool) -> Bool { - todo + todo } fn foo() -> Bool { - not == not + not == not } "#; @@ -225,11 +225,31 @@ fn illegal_inhabitants_returned() { )) } +#[test] +fn illegal_generic_instantiation() { + let source_code = r#" + type Rec { + get_t: t, + } + + + fn use_dict(dict: Rec Bool>, b: Bool) -> Bool { + let f = dict.get_t + f(b) + } + "#; + + assert!(matches!( + check_validator(parse(source_code)), + Err((_, Error::IllegalTypeInData { .. })) + )) +} + #[test] fn not_illegal_top_level_unserialisable() { let source_code = r#" fn foo() -> MillerLoopResult { - todo + todo } "#; @@ -240,11 +260,11 @@ fn not_illegal_top_level_unserialisable() { fn illegal_unserialisable_in_generic_fn() { let source_code = r#" type Foo { - foo: a + foo: a } fn main() -> Foo Bool> { - todo + todo } "#; @@ -258,16 +278,16 @@ fn illegal_unserialisable_in_generic_fn() { fn illegal_unserialisable_in_generic_miller_loop() { let source_code = r#" type Foo { - foo: a + foo: a } fn main() -> Foo { - todo + todo } "#; assert!(matches!( - check(parse(source_code)), + dbg!(check(parse(source_code))), Err((_, Error::IllegalTypeInData { .. })) )) } diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 6bab1702..2b40100b 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -744,7 +744,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { }; let spread = self.infer(*spread.base)?; - let return_type = self.instantiate(ret.clone(), &mut HashMap::new()); + let return_type = self.instantiate(ret.clone(), &mut HashMap::new(), location)?; // Check that the spread variable unifies with the return type of the constructor self.unify(return_type, spread.tipo(), spread.location(), false)?; @@ -903,7 +903,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { (module.name.clone(), constructor.clone()) }; - let tipo = self.instantiate(constructor.tipo, &mut HashMap::new()); + let tipo = self.instantiate(constructor.tipo, &mut HashMap::new(), select_location)?; let constructor = match &constructor.variant { variant @ ValueConstructorVariant::ModuleFn { name, module, .. } => { @@ -1001,9 +1001,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> { let mut type_vars = HashMap::new(); - let accessor_record_type = self.instantiate(accessor_record_type, &mut type_vars); + let accessor_record_type = + self.instantiate(accessor_record_type, &mut type_vars, record.location())?; - let tipo = self.instantiate(tipo, &mut type_vars); + let tipo = self.instantiate(tipo, &mut type_vars, record.location())?; self.unify( accessor_record_type, @@ -1078,7 +1079,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { let ann_typ = if let Some(ann) = annotation { let ann_typ = self .type_from_annotation(ann) - .map(|t| self.instantiate(t, &mut HashMap::new()))?; + .map(|t| self.instantiate(t, &mut HashMap::new(), location))??; self.unify( ann_typ.clone(), @@ -2401,7 +2402,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } = constructor; // Instantiate generic variables into unbound variables for this usage - let tipo = self.instantiate(tipo, &mut HashMap::new()); + let tipo = self.instantiate(tipo, &mut HashMap::new(), *location)?; Ok(ValueConstructor { public, @@ -2480,8 +2481,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> { }) } - fn instantiate(&mut self, t: Rc, ids: &mut HashMap>) -> Rc { - self.environment.instantiate(t, ids, &self.hydrator) + fn instantiate( + &mut self, + t: Rc, + ids: &mut HashMap>, + location: Span, + ) -> Result, Error> { + let result = self.environment.instantiate(t, ids, &self.hydrator); + ensure_serialisable(true, result.clone(), location)?; + Ok(result) } pub fn new_unbound_var(&mut self) -> Rc {