From ed9f5c6ef73b86b128adf81b8e1babe6ee1a3821 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 8 Mar 2024 12:46:42 +0100 Subject: [PATCH] Preserve TypeAlias in types for better context/feedback. --- crates/aiken-lang/src/ast.rs | 2 +- crates/aiken-lang/src/builtins.rs | 29 ++- crates/aiken-lang/src/expr.rs | 8 +- crates/aiken-lang/src/gen_uplc/tree.rs | 13 +- crates/aiken-lang/src/tipo.rs | 203 ++++++++++++++---- crates/aiken-lang/src/tipo/environment.rs | 144 ++++++++----- crates/aiken-lang/src/tipo/exhaustive.rs | 2 +- crates/aiken-lang/src/tipo/hydrator.rs | 14 +- crates/aiken-lang/src/tipo/infer.rs | 12 +- crates/aiken-lang/src/tipo/pattern.rs | 20 +- crates/aiken-lang/src/tipo/pretty.rs | 114 +++++++--- .../src/blueprint/definitions.rs | 6 +- crates/aiken-project/src/blueprint/schema.rs | 54 ++--- 13 files changed, 420 insertions(+), 201 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index b314a7ba..6dc09bab 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1197,7 +1197,7 @@ impl TypedPattern { | Pattern::Tuple { elems: elements, .. } => match &**value { - Type::Tuple { elems } => elements + Type::Tuple { elems, .. } => elements .iter() .zip(elems.iter()) .find_map(|(e, t)| e.find_node(byte_index, t)) diff --git a/crates/aiken-lang/src/builtins.rs b/crates/aiken-lang/src/builtins.rs index c0a64a5f..cf5a1135 100644 --- a/crates/aiken-lang/src/builtins.rs +++ b/crates/aiken-lang/src/builtins.rs @@ -1270,6 +1270,7 @@ pub fn int() -> Rc { name: INT.to_string(), module: "".to_string(), args: vec![], + alias: None, }) } @@ -1279,6 +1280,7 @@ pub fn data() -> Rc { name: DATA.to_string(), module: "".to_string(), args: vec![], + alias: None, }) } @@ -1288,6 +1290,7 @@ pub fn byte_array() -> Rc { public: true, name: BYTE_ARRAY.to_string(), module: "".to_string(), + alias: None, }) } @@ -1297,6 +1300,7 @@ pub fn g1_element() -> Rc { module: "".to_string(), name: G1_ELEMENT.to_string(), args: vec![], + alias: None, }) } @@ -1306,6 +1310,7 @@ pub fn g2_element() -> Rc { module: "".to_string(), name: G2_ELEMENT.to_string(), args: vec![], + alias: None, }) } @@ -1315,11 +1320,12 @@ pub fn miller_loop_result() -> Rc { module: "".to_string(), name: MILLER_LOOP_RESULT.to_string(), args: vec![], + alias: None, }) } pub fn tuple(elems: Vec>) -> Rc { - Rc::new(Type::Tuple { elems }) + Rc::new(Type::Tuple { elems, alias: None }) } pub fn bool() -> Rc { @@ -1328,6 +1334,7 @@ pub fn bool() -> Rc { public: true, name: BOOL.to_string(), module: "".to_string(), + alias: None, }) } @@ -1337,6 +1344,7 @@ pub fn prng() -> Rc { public: true, name: PRNG.to_string(), module: "".to_string(), + alias: None, }) } @@ -1344,6 +1352,7 @@ pub fn fuzzer(a: Rc) -> Rc { Rc::new(Type::Fn { args: vec![prng()], ret: option(tuple(vec![prng(), a])), + alias: Some(("Fuzzer".to_string(), vec!["a".to_string()])), }) } @@ -1353,6 +1362,7 @@ pub fn list(t: Rc) -> Rc { name: LIST.to_string(), module: "".to_string(), args: vec![t], + alias: None, }) } @@ -1362,6 +1372,7 @@ pub fn string() -> Rc { public: true, name: STRING.to_string(), module: "".to_string(), + alias: None, }) } @@ -1371,6 +1382,7 @@ pub fn void() -> Rc { public: true, name: VOID.to_string(), module: "".to_string(), + alias: None, }) } @@ -1380,6 +1392,7 @@ pub fn option(a: Rc) -> Rc { name: OPTION.to_string(), module: "".to_string(), args: vec![a], + alias: None, }) } @@ -1389,23 +1402,26 @@ pub fn ordering() -> Rc { name: ORDERING.to_string(), module: "".to_string(), args: vec![], + alias: None, }) } pub fn function(args: Vec>, ret: Rc) -> Rc { - Rc::new(Type::Fn { ret, args }) + Rc::new(Type::Fn { + ret, + args, + alias: None, + }) } pub fn generic_var(id: u64) -> Rc { let tipo = Rc::new(RefCell::new(TypeVar::Generic { id })); - - Rc::new(Type::Var { tipo }) + Rc::new(Type::Var { tipo, alias: None }) } pub fn unbound_var(id: u64) -> Rc { let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id })); - - Rc::new(Type::Var { tipo }) + Rc::new(Type::Var { tipo, alias: None }) } pub fn wrapped_redeemer(redeemer: Rc) -> Rc { @@ -1414,5 +1430,6 @@ pub fn wrapped_redeemer(redeemer: Rc) -> Rc { module: "".to_string(), name: REDEEMER_WRAPPER.to_string(), args: vec![redeemer], + alias: None, }) } diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 81798458..62deead8 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -626,7 +626,7 @@ impl UntypedExpr { &Type, ) -> Result, { - if let Type::Var { tipo: var_tipo } = tipo { + if let Type::Var { tipo: var_tipo, .. } = tipo { match &*var_tipo.borrow() { TypeVar::Link { tipo } => { return Self::reify_with(generics, data_types, t, tipo, with); @@ -724,7 +724,7 @@ impl UntypedExpr { ) } } - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + Type::Tuple { elems, .. } => Ok(UntypedExpr::Tuple { location: Span::empty(), elems: args .into_iter() @@ -740,7 +740,7 @@ impl UntypedExpr { }, uplc::ast::Constant::ProtoPair(_, _, left, right) => match tipo { - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + Type::Tuple { elems, .. } => Ok(UntypedExpr::Tuple { location: Span::empty(), elems: [left.as_ref(), right.as_ref()] .into_iter() @@ -959,7 +959,7 @@ impl UntypedExpr { ) } } - Type::Tuple { elems } => Ok(UntypedExpr::Tuple { + Type::Tuple { elems, .. } => Ok(UntypedExpr::Tuple { location: Span::empty(), elems: args .into_iter() diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index d945b53c..0cacc410 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -1,15 +1,13 @@ -use indexmap::IndexSet; -use itertools::Itertools; -use std::{borrow::BorrowMut, rc::Rc, slice::Iter}; -use uplc::{builder::EXPECT_ON_LIST, builtins::DefaultFunction}; - +use super::air::{Air, ExpectLevel}; use crate::{ ast::{BinOp, Curve, Span, UnOp}, builtins::{bool, byte_array, data, int, list, string, void}, tipo::{Type, ValueConstructor, ValueConstructorVariant}, }; - -use super::air::{Air, ExpectLevel}; +use indexmap::IndexSet; +use itertools::Itertools; +use std::{borrow::BorrowMut, rc::Rc, slice::Iter}; +use uplc::{builder::EXPECT_ON_LIST, builtins::DefaultFunction}; #[derive(Clone, Debug, PartialEq)] pub struct TreePath { @@ -720,6 +718,7 @@ impl AirTree { Type::Fn { args: vec![list(data())], ret: data(), + alias: None, } .into(), ValueConstructorVariant::ModuleFn { diff --git a/crates/aiken-lang/src/tipo.rs b/crates/aiken-lang/src/tipo.rs index 1fd00181..3fa954e5 100644 --- a/crates/aiken-lang/src/tipo.rs +++ b/crates/aiken-lang/src/tipo.rs @@ -23,7 +23,7 @@ mod pattern; mod pipe; pub mod pretty; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum Type { /// A nominal (named) type such as `Int`, `Float`, or a programmer defined /// custom type such as `Person`. The type can take other types as @@ -38,6 +38,7 @@ pub enum Type { module: String, name: String, args: Vec>, + alias: Option<(String, Vec)>, }, /// The type of a function. It takes arguments and returns a value. @@ -45,12 +46,14 @@ pub enum Type { Fn { args: Vec>, ret: Rc, + alias: Option<(String, Vec)>, }, /// A type variable. See the contained `TypeVar` enum for more information. /// Var { tipo: Rc>, + alias: Option<(String, Vec)>, }, // /// A tuple is an ordered collection of 0 or more values, each of which // /// can have a different type, so the `tuple` type is the sum of all the @@ -58,15 +61,104 @@ pub enum Type { // /// Tuple { elems: Vec>, + alias: Option<(String, Vec)>, }, } +impl PartialEq for Type { + fn eq(&self, other: &Type) -> bool { + match self { + Type::App { + public, + module, + name, + args, + .. + } => { + if let Type::App { + public: public2, + module: module2, + name: name2, + args: args2, + .. + } = other + { + name == name2 + && module == module2 + && public == public2 + && args.iter().zip(args2).all(|(left, right)| left == right) + } else { + false + } + } + + Type::Fn { args, ret, .. } => { + if let Type::Fn { + args: args2, + ret: ret2, + .. + } = other + { + ret == ret2 && args.iter().zip(args2).all(|(left, right)| left == right) + } else { + false + } + } + + Type::Tuple { elems, .. } => { + if let Type::Tuple { elems: elems2, .. } = other { + elems.iter().zip(elems2).all(|(left, right)| left == right) + } else { + false + } + } + + Type::Var { tipo, .. } => { + if let Type::Var { tipo: tipo2, .. } = other { + tipo == tipo2 + } else { + false + } + } + } + } +} + impl Type { + pub fn with_alias(tipo: Rc, alias: &Option<(String, Vec)>) -> Rc { + match alias { + None => tipo, + Some((name, args)) => tipo.as_ref().to_owned().set_alias(name, args), + } + } + + pub fn set_alias(self, name: &str, args: &[String]) -> Rc { + let alias = Some((name.to_string(), args.to_vec())); + Rc::new(match self { + Type::App { + public, + module, + name, + args, + .. + } => Type::App { + public, + module, + name, + args, + alias, + }, + Type::Fn { args, ret, .. } => Type::Fn { args, ret, alias }, + Type::Var { tipo, .. } => Type::Var { tipo, alias }, + Type::Tuple { elems, .. } => Type::Tuple { elems, alias }, + }) + } + pub fn qualifier(&self) -> Option<(String, String)> { match self { Type::App { module, name, .. } => Some((module.to_string(), name.to_string())), Type::Fn { .. } => None, - Type::Var { ref tipo } => match &*tipo.borrow() { + Type::Var { ref tipo, .. } => match &*tipo.borrow() { TypeVar::Link { ref tipo } => tipo.qualifier(), _ => None, }, @@ -86,7 +178,7 @@ impl Type { } pub fn is_unbound(&self) -> bool { - matches!(self, Self::Var { tipo } if tipo.borrow().is_unbound()) + matches!(self, Self::Var { tipo, .. } if tipo.borrow().is_unbound()) } pub fn is_function(&self) -> bool { @@ -119,7 +211,7 @@ impl Type { pub fn is_void(&self) -> bool { match self { Self::App { module, name, .. } if "Void" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_void(), + Self::Var { tipo, .. } => tipo.borrow().is_void(), _ => false, } } @@ -127,7 +219,7 @@ impl Type { pub fn is_bool(&self) -> bool { match self { Self::App { module, name, .. } if "Bool" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_bool(), + Self::Var { tipo, .. } => tipo.borrow().is_bool(), _ => false, } } @@ -135,7 +227,7 @@ impl Type { pub fn is_int(&self) -> bool { match self { Self::App { module, name, .. } if "Int" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_int(), + Self::Var { tipo, .. } => tipo.borrow().is_int(), _ => false, } } @@ -143,7 +235,7 @@ impl Type { pub fn is_bytearray(&self) -> bool { match self { Self::App { module, name, .. } if "ByteArray" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_bytearray(), + Self::Var { tipo, .. } => tipo.borrow().is_bytearray(), _ => false, } } @@ -152,7 +244,7 @@ impl Type { match self { Self::App { module, name, .. } => G1_ELEMENT == name && module.is_empty(), - Self::Var { tipo } => tipo.borrow().is_bls381_12_g1(), + Self::Var { tipo, .. } => tipo.borrow().is_bls381_12_g1(), _ => false, } } @@ -161,7 +253,7 @@ impl Type { match self { Self::App { module, name, .. } => G2_ELEMENT == name && module.is_empty(), - Self::Var { tipo } => tipo.borrow().is_bls381_12_g2(), + Self::Var { tipo, .. } => tipo.borrow().is_bls381_12_g2(), _ => false, } } @@ -170,7 +262,7 @@ impl Type { match self { Self::App { module, name, .. } => MILLER_LOOP_RESULT == name && module.is_empty(), - Self::Var { tipo } => tipo.borrow().is_ml_result(), + Self::Var { tipo, .. } => tipo.borrow().is_ml_result(), _ => false, } } @@ -178,7 +270,7 @@ impl Type { pub fn is_string(&self) -> bool { match self { Self::App { module, name, .. } if "String" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_string(), + Self::Var { tipo, .. } => tipo.borrow().is_string(), _ => false, } } @@ -186,7 +278,7 @@ impl Type { pub fn is_list(&self) -> bool { match self { Self::App { module, name, .. } if "List" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_list(), + Self::Var { tipo, .. } => tipo.borrow().is_list(), _ => false, } } @@ -194,7 +286,7 @@ impl Type { pub fn is_option(&self) -> bool { match self { Self::App { module, name, .. } if "Option" == name && module.is_empty() => true, - Self::Var { tipo } => tipo.borrow().is_option(), + Self::Var { tipo, .. } => tipo.borrow().is_option(), _ => false, } } @@ -207,14 +299,14 @@ impl Type { .first() .expect("unreachable: List should have an inner type") .is_2_tuple(), - Self::Var { tipo } => tipo.borrow().is_map(), + Self::Var { tipo, .. } => tipo.borrow().is_map(), _ => false, } } pub fn is_tuple(&self) -> bool { match self { - Type::Var { tipo } => tipo.borrow().is_tuple(), + Type::Var { tipo, .. } => tipo.borrow().is_tuple(), Type::Tuple { .. } => true, _ => false, } @@ -222,8 +314,8 @@ impl Type { pub fn is_2_tuple(&self) -> bool { match self { - Type::Var { tipo } => tipo.borrow().is_2_tuple(), - Type::Tuple { elems } => elems.len() == 2, + Type::Var { tipo, .. } => tipo.borrow().is_2_tuple(), + Type::Tuple { elems, .. } => elems.len() == 2, _ => false, } } @@ -231,7 +323,7 @@ impl Type { pub fn is_data(&self) -> bool { match self { Self::App { module, name, .. } => "Data" == name && module.is_empty(), - Self::Var { tipo } => tipo.borrow().is_data(), + Self::Var { tipo, .. } => tipo.borrow().is_data(), _ => false, } } @@ -246,15 +338,15 @@ impl Type { is_a_generic } - Type::Var { tipo } => tipo.borrow().is_generic(), - Type::Tuple { elems } => { + Type::Var { tipo, .. } => tipo.borrow().is_generic(), + Type::Tuple { elems, .. } => { let mut is_a_generic = false; for elem in elems { is_a_generic = is_a_generic || elem.is_generic(); } is_a_generic } - Type::Fn { args, ret } => { + Type::Fn { args, ret, .. } => { let mut is_a_generic = false; for arg in args { is_a_generic = is_a_generic || arg.is_generic(); @@ -268,14 +360,14 @@ impl Type { match self { Self::Fn { args, .. } => Some(args.clone()), Self::App { args, .. } => Some(args.clone()), - Self::Var { tipo } => tipo.borrow().arg_types(), + Self::Var { tipo, .. } => tipo.borrow().arg_types(), _ => None, } } pub fn get_generic(&self) -> Option { match self { - Type::Var { tipo } => tipo.borrow().get_generic(), + Type::Var { tipo, .. } => tipo.borrow().get_generic(), _ => None, } } @@ -284,24 +376,24 @@ impl Type { if self.is_list() { match self { Self::App { args, .. } => args.clone(), - Self::Var { tipo } => tipo.borrow().get_inner_types(), + Self::Var { tipo, .. } => tipo.borrow().get_inner_types(), _ => vec![], } } else if self.is_tuple() { match self { - Self::Tuple { elems } => elems.to_vec(), - Self::Var { tipo } => tipo.borrow().get_inner_types(), + Self::Tuple { elems, .. } => elems.to_vec(), + Self::Var { tipo, .. } => tipo.borrow().get_inner_types(), _ => vec![], } } else if matches!(self.get_uplc_type(), UplcType::Data) { match self { Type::App { args, .. } => args.clone(), - Type::Fn { args, ret } => { + Type::Fn { args, ret, .. } => { let mut args = args.clone(); args.push(ret.clone()); args } - Type::Var { tipo } => tipo.borrow().get_inner_types(), + Type::Var { tipo, .. } => tipo.borrow().get_inner_types(), _ => unreachable!(), } } else { @@ -324,14 +416,14 @@ impl Type { UplcType::List(UplcType::Data.into()) } else if self.is_tuple() { match self { - Self::Tuple { elems } => { + Self::Tuple { elems, .. } => { if elems.len() == 2 { UplcType::Pair(UplcType::Data.into(), UplcType::Data.into()) } else { UplcType::List(UplcType::Data.into()) } } - Self::Var { tipo } => tipo.borrow().get_uplc_type().unwrap(), + Self::Var { tipo, .. } => tipo.borrow().get_uplc_type().unwrap(), _ => unreachable!(), } } else if self.is_bls381_12_g1() { @@ -371,7 +463,7 @@ impl Type { } } - Self::Var { tipo } => { + Self::Var { tipo, alias } => { let args: Vec<_> = match tipo.borrow().deref() { TypeVar::Link { tipo } => { return tipo.get_app_args(public, module, name, arity, environment); @@ -388,10 +480,11 @@ impl Type { // to the desired type. *tipo.borrow_mut() = TypeVar::Link { tipo: Rc::new(Self::App { + public, name: name.to_string(), module: module.to_owned(), args: args.clone(), - public, + alias: alias.to_owned(), }), }; Some(args) @@ -465,7 +558,7 @@ pub fn lookup_data_type_by_tipo( data_types.get(&data_type_key).map(|item| (*item).clone()) } - Type::Var { tipo } => { + Type::Var { tipo, .. } => { if let TypeVar::Link { tipo } = &*tipo.borrow() { lookup_data_type_by_tipo(data_types, tipo) } else { @@ -500,11 +593,11 @@ pub fn get_arg_type_name(tipo: &Type) -> String { let inner_args = args.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); format!("{}_{}", name, inner_args.join("_")) } - Type::Var { tipo } => match tipo.borrow().clone() { + Type::Var { tipo, .. } => match tipo.borrow().clone() { TypeVar::Link { tipo } => get_arg_type_name(tipo.as_ref()), _ => unreachable!(), }, - Type::Tuple { elems } => { + Type::Tuple { elems, .. } => { let inner_args = elems.iter().map(|arg| get_arg_type_name(arg)).collect_vec(); inner_args.join("_") } @@ -545,6 +638,7 @@ pub fn convert_opaque_type( module, name, args, + alias, } => { let mut new_args = vec![]; for arg in args { @@ -556,10 +650,11 @@ pub fn convert_opaque_type( module: module.clone(), name: name.clone(), args: new_args, + alias: alias.clone(), } .into() } - Type::Fn { args, ret } => { + Type::Fn { args, ret, alias } => { let mut new_args = vec![]; for arg in args { let arg = convert_opaque_type(arg, data_types, deep); @@ -571,23 +666,28 @@ pub fn convert_opaque_type( Type::Fn { args: new_args, ret, + alias: alias.clone(), } .into() } - Type::Var { tipo: var_tipo } => { + Type::Var { tipo: var_tipo, .. } => { if let TypeVar::Link { tipo } = &var_tipo.borrow().clone() { convert_opaque_type(tipo, data_types, deep) } else { t.clone() } } - Type::Tuple { elems } => { + Type::Tuple { elems, alias } => { let mut new_elems = vec![]; for arg in elems { let arg = convert_opaque_type(arg, data_types, deep); new_elems.push(arg); } - Type::Tuple { elems: new_elems }.into() + Type::Tuple { + elems: new_elems, + alias: alias.clone(), + } + .into() } } } @@ -623,6 +723,7 @@ pub fn find_and_replace_generics( public, module, name, + alias, } => { let mut new_args = vec![]; for arg in args { @@ -634,10 +735,11 @@ pub fn find_and_replace_generics( public: *public, module: module.clone(), name: name.clone(), + alias: alias.clone(), }; t.into() } - Type::Fn { args, ret } => { + Type::Fn { args, ret, alias } => { let mut new_args = vec![]; for arg in args { let arg = find_and_replace_generics(arg, mono_types); @@ -649,20 +751,24 @@ pub fn find_and_replace_generics( let t = Type::Fn { args: new_args, ret, + alias: alias.clone(), }; t.into() } - Type::Tuple { elems } => { + Type::Tuple { elems, alias } => { let mut new_elems = vec![]; for elem in elems { let elem = find_and_replace_generics(elem, mono_types); new_elems.push(elem); } - let t = Type::Tuple { elems: new_elems }; + let t = Type::Tuple { + elems: new_elems, + alias: alias.clone(), + }; t.into() } - Type::Var { tipo: var_tipo } => { + Type::Var { tipo: var_tipo, .. } => { let var_type = var_tipo.as_ref().borrow().clone(); match var_type { @@ -833,10 +939,13 @@ impl TypeVar { Self::Link { tipo } => tipo.get_inner_types(), Self::Unbound { .. } => vec![], var => { - vec![Type::Var { - tipo: RefCell::new(var.clone()).into(), - } - .into()] + vec![ + Type::Var { + tipo: RefCell::new(var.clone()).into(), + alias: None, + } + .into(), + ] } } } diff --git a/crates/aiken-lang/src/tipo/environment.rs b/crates/aiken-lang/src/tipo/environment.rs index 6965bc95..fccfbbb8 100644 --- a/crates/aiken-lang/src/tipo/environment.rs +++ b/crates/aiken-lang/src/tipo/environment.rs @@ -1,9 +1,10 @@ -use std::{ - collections::{HashMap, HashSet}, - ops::Deref, - rc::Rc, +use super::{ + error::{Error, Snippet, Warning}, + exhaustive::{simplify, Matrix, PatternStack}, + hydrator::Hydrator, + AccessorsMap, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar, ValueConstructor, + ValueConstructorVariant, }; - use crate::{ ast::{ Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind, @@ -14,13 +15,10 @@ use crate::{ tipo::fields::FieldMap, IdGenerator, }; - -use super::{ - error::{Error, Snippet, Warning}, - exhaustive::{simplify, Matrix, PatternStack}, - hydrator::Hydrator, - AccessorsMap, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar, ValueConstructor, - ValueConstructorVariant, +use std::{ + collections::{HashMap, HashSet}, + ops::Deref, + rc::Rc, }; #[derive(Debug)] @@ -119,7 +117,7 @@ impl<'a> Environment<'a> { fn_location: Span, call_location: Span, ) -> Result<(Vec>, Rc), Error> { - if let Type::Var { tipo } = tipo.deref() { + if let Type::Var { tipo, .. } = tipo.deref() { let new_value = match tipo.borrow().deref() { TypeVar::Link { tipo, .. } => { return self.match_fun_type(tipo.clone(), arity, fn_location, call_location); @@ -145,7 +143,7 @@ impl<'a> Environment<'a> { } } - if let Type::Fn { args, ret } = tipo.deref() { + if let Type::Fn { args, ret, .. } = tipo.deref() { return if args.len() != arity { Err(Error::IncorrectFunctionCallArity { expected: args.len(), @@ -549,6 +547,7 @@ impl<'a> Environment<'a> { name, module, args, + alias, } => { let args = args .iter() @@ -558,15 +557,26 @@ impl<'a> Environment<'a> { public: *public, name: name.clone(), module: module.clone(), + alias: alias.clone(), args, }) } - Type::Var { tipo } => { + Type::Var { tipo, alias } => { match tipo.borrow().deref() { - TypeVar::Link { tipo } => return self.instantiate(tipo.clone(), ids, hydrator), + TypeVar::Link { tipo } => { + return Type::with_alias( + self.instantiate(tipo.clone(), ids, hydrator), + alias, + ); + } - TypeVar::Unbound { .. } => return Rc::new(Type::Var { tipo: tipo.clone() }), + TypeVar::Unbound { .. } => { + return Rc::new(Type::Var { + tipo: tipo.clone(), + alias: alias.clone(), + }); + } TypeVar::Generic { id } => match ids.get(id) { Some(t) => return t.clone(), @@ -576,27 +586,34 @@ impl<'a> Environment<'a> { let v = self.new_unbound_var(); ids.insert(*id, v.clone()); return v; - } else { - // tracing::trace!(id = id, "not_instantiating_rigid_type_var") } } }, } - Rc::new(Type::Var { tipo: tipo.clone() }) + Rc::new(Type::Var { + tipo: tipo.clone(), + alias: alias.clone(), + }) } - Type::Fn { args, ret, .. } => function( - args.iter() - .map(|t| self.instantiate(t.clone(), ids, hydrator)) - .collect(), - self.instantiate(ret.clone(), ids, hydrator), + Type::Fn { args, ret, alias } => Type::with_alias( + function( + args.iter() + .map(|t| self.instantiate(t.clone(), ids, hydrator)) + .collect(), + self.instantiate(ret.clone(), ids, hydrator), + ), + alias, ), - Type::Tuple { elems } => tuple( - elems - .iter() - .map(|t| self.instantiate(t.clone(), ids, hydrator)) - .collect(), + Type::Tuple { elems, alias } => Type::with_alias( + tuple( + elems + .iter() + .map(|t| self.instantiate(t.clone(), ids, hydrator)) + .collect(), + ), + alias, ), } } @@ -981,6 +998,7 @@ impl<'a> Environment<'a> { module: module.to_owned(), name: name.clone(), args: parameters.clone(), + alias: None, }); hydrators.insert(name.to_string(), hydrator); @@ -1024,7 +1042,11 @@ impl<'a> Environment<'a> { hydrator.disallow_new_type_variables(); // Create the type that the alias resolves to - let tipo = hydrator.type_from_annotation(resolved_type, self)?; + let tipo = hydrator + .type_from_annotation(resolved_type, self)? + .as_ref() + .to_owned() + .set_alias(name, args); self.insert_type_constructor( name.clone(), @@ -1353,13 +1375,13 @@ impl<'a> Environment<'a> { } // Collapse right hand side type links. Left hand side will be collapsed in the next block. - if let Type::Var { tipo } = t2.deref() { - if let TypeVar::Link { tipo } = tipo.borrow().deref() { + if let Type::Var { tipo, .. } = t2.deref() { + if let TypeVar::Link { tipo, .. } = tipo.borrow().deref() { return self.unify(t1, tipo.clone(), location, allow_cast); } } - if let Type::Var { tipo } = t1.deref() { + if let Type::Var { tipo, .. } = t1.deref() { enum Action { Unify(Rc), CouldNotUnify, @@ -1375,7 +1397,7 @@ impl<'a> Environment<'a> { } TypeVar::Generic { id } => { - if let Type::Var { tipo } = t2.deref() { + if let Type::Var { tipo, .. } = t2.deref() { if tipo.borrow().is_unbound() { *tipo.borrow_mut() = TypeVar::Generic { id: *id }; return Ok(()); @@ -1642,10 +1664,10 @@ pub enum EntityKind { /// could cause naively-implemented type checking to diverge. /// While traversing the type tree. fn unify_unbound_type(tipo: Rc, own_id: u64, location: Span) -> Result<(), Error> { - if let Type::Var { tipo } = tipo.deref() { + if let Type::Var { tipo, .. } = tipo.deref() { let new_value = match tipo.borrow().deref() { TypeVar::Link { tipo, .. } => { - return unify_unbound_type(tipo.clone(), own_id, location) + return unify_unbound_type(tipo.clone(), own_id, location); } TypeVar::Unbound { id } => { @@ -1674,7 +1696,7 @@ fn unify_unbound_type(tipo: Rc, own_id: u64, location: Span) -> Result<(), Ok(()) } - Type::Fn { args, ret } => { + Type::Fn { args, ret, .. } => { for arg in args { unify_unbound_type(arg.clone(), own_id, location)?; } @@ -1769,7 +1791,7 @@ pub(super) fn assert_no_labeled_arguments(args: &[CallArg]) -> Option<(Spa } pub(super) fn collapse_links(t: Rc) -> Rc { - if let Type::Var { tipo } = t.deref() { + if let Type::Var { tipo, .. } = t.deref() { if let TypeVar::Link { tipo } = tipo.borrow().deref() { return tipo.clone(); } @@ -1811,17 +1833,24 @@ fn get_compatible_record_fields( #[allow(clippy::only_used_in_recursion)] pub(crate) fn generalise(t: Rc, ctx_level: usize) -> Rc { match t.deref() { - Type::Var { tipo } => match tipo.borrow().deref() { - TypeVar::Unbound { id } => generic_var(*id), - TypeVar::Link { tipo } => generalise(tipo.clone(), ctx_level), - TypeVar::Generic { .. } => Rc::new(Type::Var { tipo: tipo.clone() }), - }, + Type::Var { tipo, alias } => Type::with_alias( + match tipo.borrow().deref() { + TypeVar::Unbound { id } => generic_var(*id), + TypeVar::Link { tipo } => generalise(tipo.clone(), ctx_level), + TypeVar::Generic { .. } => Rc::new(Type::Var { + tipo: tipo.clone(), + alias: None, + }), + }, + alias, + ), Type::App { public, module, name, args, + alias, } => { let args = args .iter() @@ -1833,21 +1862,28 @@ pub(crate) fn generalise(t: Rc, ctx_level: usize) -> Rc { module: module.clone(), name: name.clone(), args, + alias: alias.clone(), }) } - Type::Fn { args, ret } => function( - args.iter() - .map(|t| generalise(t.clone(), ctx_level)) - .collect(), - generalise(ret.clone(), ctx_level), + Type::Fn { args, ret, alias } => Type::with_alias( + function( + args.iter() + .map(|t| generalise(t.clone(), ctx_level)) + .collect(), + generalise(ret.clone(), ctx_level), + ), + alias, ), - Type::Tuple { elems } => tuple( - elems - .iter() - .map(|t| generalise(t.clone(), ctx_level)) - .collect(), + Type::Tuple { elems, alias } => Type::with_alias( + tuple( + elems + .iter() + .map(|t| generalise(t.clone(), ctx_level)) + .collect(), + ), + alias, ), } } diff --git a/crates/aiken-lang/src/tipo/exhaustive.rs b/crates/aiken-lang/src/tipo/exhaustive.rs index aa8e7d13..3a23a903 100644 --- a/crates/aiken-lang/src/tipo/exhaustive.rs +++ b/crates/aiken-lang/src/tipo/exhaustive.rs @@ -608,7 +608,7 @@ pub(super) fn simplify( Ok(Pattern::Constructor( TUPLE_NAME.to_string(), vec![tipo::ValueConstructor { - tipo: tipo::Type::Tuple { elems: vec![] }.into(), + tipo: tipo::Type::Tuple { elems: vec![], alias: None }.into(), public: true, variant: tipo::ValueConstructorVariant::Record { name: TUPLE_NAME.to_string(), diff --git a/crates/aiken-lang/src/tipo/hydrator.rs b/crates/aiken-lang/src/tipo/hydrator.rs index a6dcd029..f4d3ec04 100644 --- a/crates/aiken-lang/src/tipo/hydrator.rs +++ b/crates/aiken-lang/src/tipo/hydrator.rs @@ -1,16 +1,14 @@ -use std::{collections::HashMap, rc::Rc}; - -use crate::{ - ast::Annotation, - builtins::{function, tuple}, - tipo::Span, -}; - use super::{ environment::Environment, error::{Error, Warning}, Type, TypeConstructor, }; +use crate::{ + ast::Annotation, + builtins::{function, tuple}, + tipo::Span, +}; +use std::{collections::HashMap, rc::Rc}; /// The Hydrator takes an AST representing a type (i.e. a type annotation /// for a function argument) and returns a Type for that annotation. diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index bcc3f7dc..cf0fc5c2 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -382,10 +382,14 @@ fn infer_definition( .scope .get_mut(&f.name) .expect("Could not find preregistered type for test"); - if let Type::Fn { ref ret, .. } = scope.tipo.as_ref() { + if let Type::Fn { + ref ret, ref alias, .. + } = scope.tipo.as_ref() + { scope.tipo = Rc::new(Type::Fn { ret: ret.clone(), args: vec![inferred_inner_type.clone()], + alias: alias.clone(), }) } @@ -782,7 +786,7 @@ fn infer_fuzzer( module, name, args, .. } if module.is_empty() && name == "Option" && args.len() == 1 => { match args.first().expect("args.len() == 1").borrow() { - Type::Tuple { elems } if elems.len() == 2 => { + Type::Tuple { elems, .. } if elems.len() == 2 => { let wrapped = elems.get(1).expect("Tuple has two elements"); // NOTE: Although we've drilled through the Fuzzer structure to get here, @@ -843,7 +847,7 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result { }) } - Type::Tuple { elems } => { + Type::Tuple { elems, .. } => { let elems = elems .iter() .map(|arg| annotate_fuzzer(arg, location)) @@ -854,7 +858,7 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result { }) } - Type::Var { tipo } => match &*tipo.deref().borrow() { + Type::Var { tipo, .. } => match &*tipo.deref().borrow() { TypeVar::Link { tipo } => annotate_fuzzer(tipo, location), _ => Err(Error::GenericLeftAtBoundary { location: *location, diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index 597edf3e..cf437ec7 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -1,13 +1,5 @@ //! Type inference and checking of patterns used in case expressions //! and variables bindings. -use std::{ - collections::{HashMap, HashSet}, - ops::Deref, - rc::Rc, -}; - -use itertools::Itertools; - use super::{ environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, error::{Error, Warning}, @@ -18,6 +10,12 @@ use crate::{ ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern}, builtins::{int, list, tuple}, }; +use itertools::Itertools; +use std::{ + collections::{HashMap, HashSet}, + ops::Deref, + rc::Rc, +}; pub struct PatternTyper<'a, 'b> { environment: &'a mut Environment<'b>, @@ -238,7 +236,9 @@ impl<'a, 'b> PatternTyper<'a, 'b> { }, Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() { - Type::Tuple { elems: type_elems } => { + Type::Tuple { + elems: type_elems, .. + } => { if elems.len() != type_elems.len() { return Err(Error::IncorrectTupleArity { location, @@ -403,7 +403,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { ); match instantiated_constructor_type.deref() { - Type::Fn { args, ret } => { + Type::Fn { args, ret, .. } => { if with_spread && has_no_fields { if pattern_args.len() == args.len() { return Err(Error::UnnecessarySpreadOperator { diff --git a/crates/aiken-lang/src/tipo/pretty.rs b/crates/aiken-lang/src/tipo/pretty.rs index 892d3042..8c929598 100644 --- a/crates/aiken-lang/src/tipo/pretty.rs +++ b/crates/aiken-lang/src/tipo/pretty.rs @@ -1,12 +1,10 @@ -use std::{collections::HashMap, rc::Rc}; - -use itertools::Itertools; - use super::{Type, TypeVar}; use crate::{ docvec, pretty::{nil, *}, }; +use itertools::Itertools; +use std::{collections::HashMap, rc::Rc}; const INDENT: isize = 2; @@ -50,35 +48,75 @@ impl Printer { pub fn print<'a>(&mut self, typ: &Type) -> Document<'a> { match typ { Type::App { - name, args, module, .. - } => { - let doc = if self.name_clashes_if_unqualified(name, module) { - qualify_type_name(module, name) - } else { - self.printed_types.insert(name.clone(), module.clone()); - Document::String(name.clone()) - }; - if args.is_empty() { - doc - } else { - doc.append("<") - .append(self.args_to_aiken_doc(args)) - .append(">") + name, + args, + module, + alias, + .. + } => match alias { + Some(alias) => self.type_alias_doc(alias.clone()), + None => { + let doc = if self.name_clashes_if_unqualified(name, module) { + qualify_type_name(module, name) + } else { + self.printed_types.insert(name.clone(), module.clone()); + Document::String(name.clone()) + }; + if args.is_empty() { + doc + } else { + doc.append("<") + .append(self.args_to_aiken_doc(args)) + .append(">") + } } - } + }, - Type::Fn { args, ret } => "fn(" - .to_doc() - .append(self.args_to_aiken_doc(args)) - .append(") ->") - .append(break_("", " ").append(self.print(ret)).nest(INDENT).group()), + Type::Fn { args, ret, alias } => match alias { + Some(alias) => self.type_alias_doc(alias.clone()), + None => "fn(" + .to_doc() + .append(self.args_to_aiken_doc(args)) + .append(") ->") + .append(break_("", " ").append(self.print(ret)).nest(INDENT).group()), + }, - Type::Var { tipo: typ, .. } => self.type_var_doc(&typ.borrow()), + Type::Var { tipo: typ, alias } => match alias { + Some(alias) => self.type_alias_doc(alias.clone()), + None => self.type_var_doc(&typ.borrow()), + }, - Type::Tuple { elems, .. } => self.args_to_aiken_doc(elems).surround("(", ")"), + Type::Tuple { elems, alias } => match alias { + Some(alias) => self.type_alias_doc(alias.clone()), + None => self.args_to_aiken_doc(elems).surround("(", ")"), + }, } } + fn type_alias_doc<'a>(&mut self, alias: (String, Vec)) -> Document<'a> { + let mut doc = Document::String(alias.0.to_owned()); + + if !alias.1.is_empty() { + let args = concat(Itertools::intersperse( + alias.1.into_iter().map(Document::String), + break_(",", ", "), + )); + + doc = doc + .append("<") + .append( + break_("", "") + .append(args) + .nest(INDENT) + .append(break_(",", "")) + .group(), + ) + .append(">"); + } + + doc + } + fn name_clashes_if_unqualified(&mut self, tipo: &String, module: &String) -> bool { match self.printed_types.get(tipo) { None => false, @@ -169,13 +207,10 @@ fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> { #[cfg(test)] mod tests { - use std::cell::RefCell; - - use pretty_assertions::assert_eq; - - use crate::builtins::{function, int}; - use super::*; + use crate::builtins::{function, int}; + use pretty_assertions::assert_eq; + use std::cell::RefCell; #[test] fn next_letter_test() { @@ -275,6 +310,7 @@ mod tests { name: "Int".to_string(), public: true, args: vec![], + alias: None }, "Int", ); @@ -283,18 +319,21 @@ mod tests { module: "".to_string(), name: "Pair".to_string(), public: true, + alias: None, args: vec![ Rc::new(Type::App { module: "whatever".to_string(), name: "Int".to_string(), public: true, args: vec![], + alias: None }), Rc::new(Type::App { module: "whatever".to_string(), name: "Bool".to_string(), public: true, args: vec![], + alias: None }), ], }, @@ -308,12 +347,14 @@ mod tests { module: "whatever".to_string(), name: "Int".to_string(), public: true, + alias: None, }), Rc::new(Type::App { args: vec![], module: "whatever".to_string(), name: "Bool".to_string(), public: true, + alias: None, }), ], ret: Rc::new(Type::App { @@ -321,14 +362,18 @@ mod tests { module: "whatever".to_string(), name: "Bool".to_string(), public: true, + alias: None, }), + alias: None, }, "fn(Int, Bool) -> Bool", ); assert_string!( Type::Var { + alias: None, tipo: Rc::new(RefCell::new(TypeVar::Link { tipo: Rc::new(Type::App { + alias: None, args: vec![], module: "whatever".to_string(), name: "Int".to_string(), @@ -341,6 +386,7 @@ mod tests { assert_string!( Type::Var { tipo: Rc::new(RefCell::new(TypeVar::Unbound { id: 2231 })), + alias: None, }, "a", ); @@ -348,9 +394,11 @@ mod tests { function( vec![Rc::new(Type::Var { tipo: Rc::new(RefCell::new(TypeVar::Unbound { id: 78 })), + alias: None, })], Rc::new(Type::Var { tipo: Rc::new(RefCell::new(TypeVar::Unbound { id: 2 })), + alias: None, }), ), "fn(a) -> b", @@ -359,9 +407,11 @@ mod tests { function( vec![Rc::new(Type::Var { tipo: Rc::new(RefCell::new(TypeVar::Generic { id: 78 })), + alias: None, })], Rc::new(Type::Var { tipo: Rc::new(RefCell::new(TypeVar::Generic { id: 2 })), + alias: None, }), ), "fn(a) -> b", diff --git a/crates/aiken-project/src/blueprint/definitions.rs b/crates/aiken-project/src/blueprint/definitions.rs index 7893d9fd..0a290de4 100644 --- a/crates/aiken-project/src/blueprint/definitions.rs +++ b/crates/aiken-project/src/blueprint/definitions.rs @@ -139,7 +139,7 @@ impl Reference { } } - Type::Tuple { elems } => Self { + Type::Tuple { elems, .. } => Self { inner: format!( "Tuple{elems}", elems = Self::from_types(elems, type_parameters) @@ -150,7 +150,7 @@ impl Reference { // // Implementations below are only there for completeness. In practice, we should never // end up creating references for 'Var' or 'Fn' in the context of blueprints. - Type::Var { tipo } => match tipo.borrow().deref() { + Type::Var { tipo, .. } => match tipo.borrow().deref() { TypeVar::Link { tipo } => Self::from_type(tipo.as_ref(), type_parameters), TypeVar::Generic { id } | TypeVar::Unbound { id } => { let tipo = type_parameters.get(id).unwrap(); @@ -158,7 +158,7 @@ impl Reference { } }, - Type::Fn { args, ret } => Self { + Type::Fn { args, ret, .. } => Self { inner: format!( "Fn{args}_{ret}", args = Self::from_types(args, type_parameters), diff --git a/crates/aiken-project/src/blueprint/schema.rs b/crates/aiken-project/src/blueprint/schema.rs index dc40915c..3f7538ac 100644 --- a/crates/aiken-project/src/blueprint/schema.rs +++ b/crates/aiken-project/src/blueprint/schema.rs @@ -350,7 +350,7 @@ impl Annotated { annotated, }) }), - Type::Tuple { elems } => { + Type::Tuple { elems, .. } => { definitions.register(type_info, &type_parameters.clone(), |definitions| { let elems = elems .iter() @@ -368,7 +368,7 @@ impl Annotated { }) }) } - Type::Var { tipo } => match tipo.borrow().deref() { + Type::Var { tipo, .. } => match tipo.borrow().deref() { TypeVar::Link { tipo } => { Annotated::do_from_type(tipo, modules, type_parameters, definitions) } @@ -440,7 +440,7 @@ fn collect_type_parameters<'a>( ) { for (index, generic) in generics.iter().enumerate() { match &**generic { - Type::Var { tipo } => match *tipo.borrow() { + Type::Var { tipo, .. } => match *tipo.borrow() { TypeVar::Generic { id } => { type_parameters.insert( id, @@ -1125,11 +1125,13 @@ pub mod tests { #[test] fn serialize_data_constr_1() { - let schema = Schema::Data(Data::AnyOf(vec![Constructor { - index: 0, - fields: vec![], - } - .into()])); + let schema = Schema::Data(Data::AnyOf(vec![ + Constructor { + index: 0, + fields: vec![], + } + .into(), + ])); assert_json( &schema, json!({ @@ -1290,14 +1292,16 @@ pub mod tests { #[test] fn deserialize_any_of() { assert_eq!( - Data::AnyOf(vec![Constructor { - index: 0, - fields: vec![ - Declaration::Referenced(Reference::new("foo")).into(), - Declaration::Referenced(Reference::new("bar")).into() - ], - } - .into()]), + Data::AnyOf(vec![ + Constructor { + index: 0, + fields: vec![ + Declaration::Referenced(Reference::new("foo")).into(), + Declaration::Referenced(Reference::new("bar")).into() + ], + } + .into() + ]), serde_json::from_value(json!({ "anyOf": [{ "index": 0, @@ -1318,14 +1322,16 @@ pub mod tests { #[test] fn deserialize_one_of() { assert_eq!( - Data::AnyOf(vec![Constructor { - index: 0, - fields: vec![ - Declaration::Referenced(Reference::new("foo")).into(), - Declaration::Referenced(Reference::new("bar")).into() - ], - } - .into()]), + Data::AnyOf(vec![ + Constructor { + index: 0, + fields: vec![ + Declaration::Referenced(Reference::new("foo")).into(), + Declaration::Referenced(Reference::new("bar")).into() + ], + } + .into() + ]), serde_json::from_value(json!({ "oneOf": [{ "index": 0,