Merge pull request #875 from aiken-lang/rvcas/expect_opaque

block expects on opaque types
This commit is contained in:
Matthias Benkort 2024-03-14 19:43:47 +01:00 committed by GitHub
commit 3f254dbe6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 581 additions and 172 deletions

View File

@ -1267,6 +1267,7 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
pub fn int() -> Rc<Type> { pub fn int() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
name: INT.to_string(), name: INT.to_string(),
module: "".to_string(), module: "".to_string(),
args: vec![], args: vec![],
@ -1277,6 +1278,7 @@ pub fn int() -> Rc<Type> {
pub fn data() -> Rc<Type> { pub fn data() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
name: DATA.to_string(), name: DATA.to_string(),
module: "".to_string(), module: "".to_string(),
args: vec![], args: vec![],
@ -1288,6 +1290,7 @@ pub fn byte_array() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],
public: true, public: true,
contains_opaque: false,
name: BYTE_ARRAY.to_string(), name: BYTE_ARRAY.to_string(),
module: "".to_string(), module: "".to_string(),
alias: None, alias: None,
@ -1297,6 +1300,7 @@ pub fn byte_array() -> Rc<Type> {
pub fn g1_element() -> Rc<Type> { pub fn g1_element() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: G1_ELEMENT.to_string(), name: G1_ELEMENT.to_string(),
args: vec![], args: vec![],
@ -1307,6 +1311,7 @@ pub fn g1_element() -> Rc<Type> {
pub fn g2_element() -> Rc<Type> { pub fn g2_element() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: G2_ELEMENT.to_string(), name: G2_ELEMENT.to_string(),
args: vec![], args: vec![],
@ -1317,6 +1322,7 @@ pub fn g2_element() -> Rc<Type> {
pub fn miller_loop_result() -> Rc<Type> { pub fn miller_loop_result() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: MILLER_LOOP_RESULT.to_string(), name: MILLER_LOOP_RESULT.to_string(),
args: vec![], args: vec![],
@ -1332,6 +1338,7 @@ pub fn bool() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],
public: true, public: true,
contains_opaque: false,
name: BOOL.to_string(), name: BOOL.to_string(),
module: "".to_string(), module: "".to_string(),
alias: None, alias: None,
@ -1342,6 +1349,7 @@ pub fn prng() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],
public: true, public: true,
contains_opaque: false,
name: PRNG.to_string(), name: PRNG.to_string(),
module: "".to_string(), module: "".to_string(),
alias: None, alias: None,
@ -1355,6 +1363,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
name: "PRNG".to_string(), name: "PRNG".to_string(),
arguments: vec![], arguments: vec![],
}; };
Rc::new(Type::Fn { Rc::new(Type::Fn {
args: vec![prng()], args: vec![prng()],
ret: option(tuple(vec![prng(), a])), ret: option(tuple(vec![prng(), a])),
@ -1391,6 +1400,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
pub fn list(t: Rc<Type>) -> Rc<Type> { pub fn list(t: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
name: LIST.to_string(), name: LIST.to_string(),
module: "".to_string(), module: "".to_string(),
args: vec![t], args: vec![t],
@ -1402,6 +1412,7 @@ pub fn string() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],
public: true, public: true,
contains_opaque: false,
name: STRING.to_string(), name: STRING.to_string(),
module: "".to_string(), module: "".to_string(),
alias: None, alias: None,
@ -1412,6 +1423,7 @@ pub fn void() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],
public: true, public: true,
contains_opaque: false,
name: VOID.to_string(), name: VOID.to_string(),
module: "".to_string(), module: "".to_string(),
alias: None, alias: None,
@ -1421,6 +1433,7 @@ pub fn void() -> Rc<Type> {
pub fn option(a: Rc<Type>) -> Rc<Type> { pub fn option(a: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
name: OPTION.to_string(), name: OPTION.to_string(),
module: "".to_string(), module: "".to_string(),
args: vec![a], args: vec![a],
@ -1431,6 +1444,7 @@ pub fn option(a: Rc<Type>) -> Rc<Type> {
pub fn ordering() -> Rc<Type> { pub fn ordering() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
name: ORDERING.to_string(), name: ORDERING.to_string(),
module: "".to_string(), module: "".to_string(),
args: vec![], args: vec![],
@ -1448,17 +1462,20 @@ pub fn function(args: Vec<Rc<Type>>, ret: Rc<Type>) -> Rc<Type> {
pub fn generic_var(id: u64) -> Rc<Type> { pub fn generic_var(id: u64) -> Rc<Type> {
let tipo = Rc::new(RefCell::new(TypeVar::Generic { id })); let tipo = Rc::new(RefCell::new(TypeVar::Generic { id }));
Rc::new(Type::Var { tipo, alias: None }) Rc::new(Type::Var { tipo, alias: None })
} }
pub fn unbound_var(id: u64) -> Rc<Type> { pub fn unbound_var(id: u64) -> Rc<Type> {
let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id })); let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id }));
Rc::new(Type::Var { tipo, alias: None }) Rc::new(Type::Var { tipo, alias: None })
} }
pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> { pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: REDEEMER_WRAPPER.to_string(), name: REDEEMER_WRAPPER.to_string(),
args: vec![redeemer], args: vec![redeemer],

View File

@ -16,6 +16,7 @@ fn parse(source_code: &str) -> UntypedModule {
fn check_module( fn check_module(
ast: UntypedModule, ast: UntypedModule,
extra: Vec<(String, UntypedModule)>,
kind: ModuleKind, kind: ModuleKind,
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> { ) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
let id_gen = IdGenerator::new(); let id_gen = IdGenerator::new();
@ -26,6 +27,21 @@ fn check_module(
module_types.insert("aiken".to_string(), builtins::prelude(&id_gen)); module_types.insert("aiken".to_string(), builtins::prelude(&id_gen));
module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen)); module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen));
for (package, module) in extra {
let mut warnings = vec![];
let typed_module = module
.infer(
&id_gen,
kind,
&package,
&module_types,
Tracing::All(TraceLevel::Verbose),
&mut warnings,
)
.expect("extra dependency did not compile");
module_types.insert(package.clone(), typed_module.type_info.clone());
}
let result = ast.infer( let result = ast.infer(
&id_gen, &id_gen,
kind, kind,
@ -41,13 +57,20 @@ fn check_module(
} }
fn check(ast: UntypedModule) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> { fn check(ast: UntypedModule) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
check_module(ast, ModuleKind::Lib) check_module(ast, Vec::new(), ModuleKind::Lib)
}
fn check_with_deps(
ast: UntypedModule,
extra: Vec<(String, UntypedModule)>,
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
check_module(ast, extra, ModuleKind::Lib)
} }
fn check_validator( fn check_validator(
ast: UntypedModule, ast: UntypedModule,
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> { ) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
check_module(ast, ModuleKind::Validator) check_module(ast, Vec::new(), ModuleKind::Validator)
} }
#[test] #[test]
@ -1793,3 +1816,171 @@ fn backpassing_type_annotation() {
assert!(check(parse(source_code)).is_ok()) assert!(check(parse(source_code)).is_ok())
} }
#[test]
fn forbid_expect_into_opaque_type_from_data() {
let source_code = r#"
opaque type Thing { inner: Int }
fn bar(n: Data) {
expect a: Thing = n
a
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn forbid_expect_into_opaque_type_constructor_without_typecasting_in_module() {
let source_code = r#"
opaque type Thing {
Foo(Int)
Bar(Int)
}
fn bar(thing: Thing) {
expect Foo(a) = thing
a
}
"#;
assert!(check(parse(source_code)).is_ok());
}
#[test]
fn forbid_importing_or_using_opaque_constructors() {
let dependency = r#"
pub opaque type Thing {
Foo(Int)
Bar(Int)
}
"#;
let source_code = r#"
use foo/thing.{Thing, Foo}
fn bar(thing: Thing) {
expect Foo(a) = thing
a
}
"#;
assert!(matches!(
check_with_deps(
parse(source_code),
vec![("foo/thing".to_string(), parse(dependency))],
),
Err((_, Error::UnknownModuleField { .. })),
));
let source_code = r#"
use foo/thing.{Thing}
fn bar(thing: Thing) {
expect Foo(a) = thing
a
}
"#;
assert!(matches!(
check_with_deps(
parse(source_code),
vec![("foo/thing".to_string(), parse(dependency))],
),
Err((_, Error::UnknownTypeConstructor { .. })),
));
}
#[test]
fn forbid_expect_into_opaque_type_constructor_with_typecasting() {
let source_code = r#"
opaque type Thing {
Foo(Int)
Bar(Int)
}
fn bar(data: Data) {
expect Foo(a): Thing = data
a
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn forbid_expect_into_nested_opaque_in_record_without_typecasting() {
let source_code = r#"
opaque type Thing { inner: Int }
type Foo { foo: Thing }
fn bar(thing: Foo) {
expect Foo { foo: Thing { inner } } : Foo = thing
Void
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn forbid_expect_into_nested_opaque_in_record_with_typecasting() {
let source_code = r#"
opaque type Thing { inner: Int }
type Foo { foo: Thing }
fn bar(a: Data) {
expect Foo { foo: Thing { inner } } : Foo = a
Void
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn forbid_expect_into_nested_opaque_in_list() {
let source_code = r#"
opaque type Thing { inner: Int }
fn bar(a: Data) {
expect [x]: List<Thing> = [a]
Void
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn allow_expect_on_var_patterns_that_are_opaque() {
let source_code = r#"
opaque type Thing { inner: Int }
fn bar(a: Option<Thing>) {
expect Some(thing) = a
thing.inner
}
"#;
assert!(check(parse(source_code)).is_ok())
}

View File

@ -42,6 +42,7 @@ pub enum Type {
/// ///
App { App {
public: bool, public: bool,
contains_opaque: bool,
module: String, module: String,
name: String, name: String,
args: Vec<Rc<Type>>, args: Vec<Rc<Type>>,
@ -80,19 +81,22 @@ impl PartialEq for Type {
module, module,
name, name,
args, args,
.. contains_opaque: opaque,
alias: _,
} => { } => {
if let Type::App { if let Type::App {
public: public2, public: public2,
module: module2, module: module2,
name: name2, name: name2,
args: args2, args: args2,
.. contains_opaque: opaque2,
alias: _,
} = other } = other
{ {
name == name2 name == name2
&& module == module2 && module == module2
&& public == public2 && public == public2
&& opaque == opaque2
&& args.iter().zip(args2).all(|(left, right)| left == right) && args.iter().zip(args2).all(|(left, right)| left == right)
} else { } else {
false false
@ -103,7 +107,7 @@ impl PartialEq for Type {
if let Type::Fn { if let Type::Fn {
args: args2, args: args2,
ret: ret2, ret: ret2,
.. alias: _,
} = other } = other
{ {
ret == ret2 && args.iter().zip(args2).all(|(left, right)| left == right) ret == ret2 && args.iter().zip(args2).all(|(left, right)| left == right)
@ -112,7 +116,7 @@ impl PartialEq for Type {
} }
} }
Type::Tuple { elems, .. } => { Type::Tuple { elems, alias: _ } => {
if let Type::Tuple { elems: elems2, .. } = other { if let Type::Tuple { elems: elems2, .. } = other {
elems.iter().zip(elems2).all(|(left, right)| left == right) elems.iter().zip(elems2).all(|(left, right)| left == right)
} else { } else {
@ -120,8 +124,12 @@ impl PartialEq for Type {
} }
} }
Type::Var { tipo, .. } => { Type::Var { tipo, alias: _ } => {
if let Type::Var { tipo: tipo2, .. } = other { if let Type::Var {
tipo: tipo2,
alias: _,
} = other
{
tipo == tipo2 tipo == tipo2
} else { } else {
false false
@ -152,20 +160,26 @@ impl Type {
Rc::new(match self { Rc::new(match self {
Type::App { Type::App {
public, public,
contains_opaque: opaque,
module, module,
name, name,
args, args,
.. alias: _,
} => Type::App { } => Type::App {
public, public,
contains_opaque: opaque,
module, module,
name, name,
args, args,
alias, alias,
}, },
Type::Fn { args, ret, .. } => Type::Fn { args, ret, alias }, Type::Fn {
Type::Var { tipo, .. } => Type::Var { tipo, alias }, args,
Type::Tuple { elems, .. } => Type::Tuple { elems, alias }, ret,
alias: _,
} => Type::Fn { args, ret, alias },
Type::Var { tipo, alias: _ } => Type::Var { tipo, alias },
Type::Tuple { elems, alias: _ } => Type::Tuple { elems, alias },
}) })
} }
@ -181,15 +195,28 @@ impl Type {
} }
} }
pub fn is_result_constructor(&self) -> bool { pub fn contains_opaque(&self) -> bool {
match self { match self {
Type::Fn { ret, .. } => ret.is_result(), Type::Var { tipo, .. } => tipo.borrow().is_or_holds_opaque(),
_ => false, Type::App {
contains_opaque: opaque,
args,
..
} => *opaque || args.iter().any(|arg| arg.contains_opaque()),
Type::Tuple { elems, .. } => elems.iter().any(|elem| elem.contains_opaque()),
Type::Fn { .. } => false,
} }
} }
pub fn is_result(&self) -> bool { pub fn set_opaque(&mut self, opaque: bool) {
matches!(self, Self::App { name, module, .. } if "Result" == name && module.is_empty()) match self {
Type::App {
contains_opaque, ..
} => {
*contains_opaque = opaque;
}
Type::Fn { .. } | Type::Var { .. } | Type::Tuple { .. } => (),
}
} }
pub fn is_unbound(&self) -> bool { pub fn is_unbound(&self) -> bool {
@ -459,6 +486,7 @@ impl Type {
pub fn get_app_args( pub fn get_app_args(
&self, &self,
public: bool, public: bool,
opaque: bool,
module: &str, module: &str,
name: &str, name: &str,
arity: usize, arity: usize,
@ -481,7 +509,7 @@ impl Type {
Self::Var { tipo, alias } => { Self::Var { tipo, alias } => {
let args: Vec<_> = match tipo.borrow().deref() { let args: Vec<_> = match tipo.borrow().deref() {
TypeVar::Link { tipo } => { TypeVar::Link { tipo } => {
return tipo.get_app_args(public, module, name, arity, environment); return tipo.get_app_args(public, opaque, module, name, arity, environment);
} }
TypeVar::Unbound { .. } => { TypeVar::Unbound { .. } => {
@ -496,6 +524,7 @@ impl Type {
*tipo.borrow_mut() = TypeVar::Link { *tipo.borrow_mut() = TypeVar::Link {
tipo: Rc::new(Self::App { tipo: Rc::new(Self::App {
public, public,
contains_opaque: opaque,
name: name.to_string(), name: name.to_string(),
module: module.to_owned(), module: module.to_owned(),
args: args.clone(), args: args.clone(),
@ -650,6 +679,7 @@ pub fn convert_opaque_type(
match t.as_ref() { match t.as_ref() {
Type::App { Type::App {
public, public,
contains_opaque: opaque,
module, module,
name, name,
args, args,
@ -662,6 +692,7 @@ pub fn convert_opaque_type(
} }
Type::App { Type::App {
public: *public, public: *public,
contains_opaque: *opaque,
module: module.clone(), module: module.clone(),
name: name.clone(), name: name.clone(),
args: new_args, args: new_args,
@ -736,6 +767,7 @@ pub fn find_and_replace_generics(
Type::App { Type::App {
args, args,
public, public,
contains_opaque: opaque,
module, module,
name, name,
alias, alias,
@ -748,6 +780,7 @@ pub fn find_and_replace_generics(
let t = Type::App { let t = Type::App {
args: new_args, args: new_args,
public: *public, public: *public,
contains_opaque: *opaque,
module: module.clone(), module: module.clone(),
name: name.clone(), name: name.clone(),
alias: alias.clone(), alias: alias.clone(),
@ -829,6 +862,13 @@ impl TypeVar {
matches!(self, Self::Unbound { .. }) matches!(self, Self::Unbound { .. })
} }
pub fn is_or_holds_opaque(&self) -> bool {
match self {
Self::Link { tipo } => tipo.contains_opaque(),
_ => false,
}
}
pub fn is_void(&self) -> bool { pub fn is_void(&self) -> bool {
match self { match self {
Self::Link { tipo } => tipo.is_void(), Self::Link { tipo } => tipo.is_void(),

View File

@ -145,7 +145,12 @@ impl<'a> Environment<'a> {
} }
} }
if let Type::Fn { args, ret, .. } = tipo.deref() { if let Type::Fn {
args,
ret,
alias: _,
} = tipo.deref()
{
return if args.len() != arity { return if args.len() != arity {
Err(Error::IncorrectFunctionCallArity { Err(Error::IncorrectFunctionCallArity {
expected: args.len(), expected: args.len(),
@ -296,6 +301,25 @@ impl<'a> Environment<'a> {
} }
} }
pub fn get_type_constructor_mut(
&mut self,
name: &str,
location: Span,
) -> Result<&mut TypeConstructor, Error> {
let types = self.module_types.keys().map(|t| t.to_string()).collect();
let constructor = self
.module_types
.get_mut(name)
.ok_or_else(|| Error::UnknownType {
location,
name: name.to_string(),
types,
})?;
Ok(constructor)
}
/// Lookup a type in the current scope. /// Lookup a type in the current scope.
pub fn get_type_constructor( pub fn get_type_constructor(
&mut self, &mut self,
@ -546,6 +570,7 @@ impl<'a> Environment<'a> {
match t.deref() { match t.deref() {
Type::App { Type::App {
public, public,
contains_opaque: opaque,
name, name,
module, module,
args, args,
@ -555,8 +580,10 @@ impl<'a> Environment<'a> {
.iter() .iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator)) .map(|t| self.instantiate(t.clone(), ids, hydrator))
.collect(); .collect();
Rc::new(Type::App { Rc::new(Type::App {
public: *public, public: *public,
contains_opaque: *opaque,
name: name.clone(), name: name.clone(),
module: module.clone(), module: module.clone(),
alias: alias.clone(), alias: alias.clone(),
@ -727,7 +754,7 @@ impl<'a> Environment<'a> {
as_name, as_name,
unqualified, unqualified,
location, location,
.. package: _,
}) => { }) => {
let name = module.join("/"); let name = module.join("/");
@ -762,7 +789,6 @@ impl<'a> Environment<'a> {
name, name,
location, location,
as_name, as_name,
..
} in unqualified } in unqualified
{ {
let mut type_imported = false; let mut type_imported = false;
@ -983,10 +1009,12 @@ impl<'a> Environment<'a> {
Definition::DataType(DataType { Definition::DataType(DataType {
name, name,
public, public,
opaque,
parameters, parameters,
location, location,
constructors, constructors,
.. doc: _,
typed_parameters: _,
}) => { }) => {
assert_unique_type_name(names, name, location)?; assert_unique_type_name(names, name, location)?;
@ -997,6 +1025,7 @@ impl<'a> Environment<'a> {
let tipo = Rc::new(Type::App { let tipo = Rc::new(Type::App {
public: *public, public: *public,
contains_opaque: *opaque,
module: module.to_owned(), module: module.to_owned(),
name: name.clone(), name: name.clone(),
args: parameters.clone(), args: parameters.clone(),
@ -1032,7 +1061,8 @@ impl<'a> Environment<'a> {
parameters: args, parameters: args,
alias: name, alias: name,
annotation: resolved_type, annotation: resolved_type,
.. doc: _,
tipo: _,
}) => { }) => {
assert_unique_type_name(names, name, location)?; assert_unique_type_name(names, name, location)?;
@ -1173,7 +1203,9 @@ impl<'a> Environment<'a> {
fun, fun,
other_fun, other_fun,
params, params,
.. doc: _,
location: _,
end_position: _,
}) if kind.is_validator() => { }) if kind.is_validator() => {
let default_annotation = |mut arg: UntypedArg| { let default_annotation = |mut arg: UntypedArg| {
if arg.annotation.is_none() { if arg.annotation.is_none() {
@ -1251,7 +1283,10 @@ impl<'a> Environment<'a> {
opaque, opaque,
name, name,
constructors, constructors,
.. doc: _,
location: _,
parameters: _,
typed_parameters: _,
}) => { }) => {
let mut hydrator = hydrators let mut hydrator = hydrators
.remove(name) .remove(name)
@ -1294,7 +1329,8 @@ impl<'a> Environment<'a> {
label, label,
annotation, annotation,
location, location,
.. tipo: _,
doc: _,
}, },
) in constructor.arguments.iter().enumerate() ) in constructor.arguments.iter().enumerate()
{ {
@ -1379,18 +1415,28 @@ impl<'a> Environment<'a> {
&& !(t1.is_function() || t2.is_function()) && !(t1.is_function() || t2.is_function())
&& !(t1.is_generic() || t2.is_generic()) && !(t1.is_generic() || t2.is_generic())
&& !(t1.is_string() || t2.is_string()) && !(t1.is_string() || t2.is_string())
&& !(t1.contains_opaque() || t2.contains_opaque())
{ {
return Ok(()); return Ok(());
} }
if allow_cast && (t1.contains_opaque() || t2.contains_opaque()) {
return Err(Error::ExpectOnOpaqueType { location });
}
// Collapse right hand side type links. Left hand side will be collapsed in the next block. // 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 Type::Var { tipo, alias } = t2.deref() {
if let TypeVar::Link { tipo, .. } = tipo.borrow().deref() { if let TypeVar::Link { tipo } = tipo.borrow().deref() {
return self.unify(t1, tipo.clone(), location, allow_cast); return self.unify(
t1,
Type::with_alias(tipo.clone(), alias.clone()),
location,
allow_cast,
);
} }
} }
if let Type::Var { tipo, .. } = t1.deref() { if let Type::Var { tipo, alias } = t1.deref() {
enum Action { enum Action {
Unify(Rc<Type>), Unify(Rc<Type>),
CouldNotUnify, CouldNotUnify,
@ -1398,7 +1444,9 @@ impl<'a> Environment<'a> {
} }
let action = match tipo.borrow().deref() { let action = match tipo.borrow().deref() {
TypeVar::Link { tipo } => Action::Unify(tipo.clone()), TypeVar::Link { tipo } => {
Action::Unify(Type::with_alias(tipo.clone(), alias.clone()))
}
TypeVar::Unbound { id } => { TypeVar::Unbound { id } => {
unify_unbound_type(t2.clone(), *id, location)?; unify_unbound_type(t2.clone(), *id, location)?;
@ -1406,7 +1454,7 @@ impl<'a> Environment<'a> {
} }
TypeVar::Generic { id } => { TypeVar::Generic { id } => {
if let Type::Var { tipo, .. } = t2.deref() { if let Type::Var { tipo, alias: _ } = t2.deref() {
if tipo.borrow().is_unbound() { if tipo.borrow().is_unbound() {
*tipo.borrow_mut() = TypeVar::Generic { id: *id }; *tipo.borrow_mut() = TypeVar::Generic { id: *id };
return Ok(()); return Ok(());
@ -1446,13 +1494,17 @@ impl<'a> Environment<'a> {
module: m1, module: m1,
name: n1, name: n1,
args: args1, args: args1,
.. public: _,
contains_opaque: _,
alias: _,
}, },
Type::App { Type::App {
module: m2, module: m2,
name: n2, name: n2,
args: args2, args: args2,
.. public: _,
contains_opaque: _,
alias: _,
}, },
) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => { ) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => {
for (a, b) in args1.iter().zip(args2) { for (a, b) in args1.iter().zip(args2) {
@ -1465,9 +1517,16 @@ impl<'a> Environment<'a> {
Ok(()) Ok(())
} }
(Type::Tuple { elems: elems1, .. }, Type::Tuple { elems: elems2, .. }) (
if elems1.len() == elems2.len() => Type::Tuple {
{ elems: elems1,
alias: _,
},
Type::Tuple {
elems: elems2,
alias: _,
},
) if elems1.len() == elems2.len() => {
for (a, b) in elems1.iter().zip(elems2) { for (a, b) in elems1.iter().zip(elems2) {
unify_enclosed_type( unify_enclosed_type(
t1.clone(), t1.clone(),
@ -1482,12 +1541,12 @@ impl<'a> Environment<'a> {
Type::Fn { Type::Fn {
args: args1, args: args1,
ret: retrn1, ret: retrn1,
.. alias: _,
}, },
Type::Fn { Type::Fn {
args: args2, args: args2,
ret: retrn2, ret: retrn2,
.. alias: _,
}, },
) if args1.len() == args2.len() => { ) if args1.len() == args2.len() => {
for (a, b) in args1.iter().zip(args2) { for (a, b) in args1.iter().zip(args2) {
@ -1673,10 +1732,14 @@ pub enum EntityKind {
/// could cause naively-implemented type checking to diverge. /// could cause naively-implemented type checking to diverge.
/// While traversing the type tree. /// While traversing the type tree.
fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(), Error> { fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(), Error> {
if let Type::Var { tipo, .. } = tipo.deref() { if let Type::Var { tipo, alias } = tipo.deref() {
let new_value = match tipo.borrow().deref() { let new_value = match tipo.borrow().deref() {
TypeVar::Link { tipo, .. } => { TypeVar::Link { tipo } => {
return unify_unbound_type(tipo.clone(), own_id, location); return unify_unbound_type(
Type::with_alias(tipo.clone(), alias.clone()),
own_id,
location,
);
} }
TypeVar::Unbound { id } => { TypeVar::Unbound { id } => {
@ -1697,7 +1760,14 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
} }
match tipo.deref() { match tipo.deref() {
Type::App { args, .. } => { Type::App {
args,
module: _,
name: _,
public: _,
alias: _,
contains_opaque: _,
} => {
for arg in args { for arg in args {
unify_unbound_type(arg.clone(), own_id, location)? unify_unbound_type(arg.clone(), own_id, location)?
} }
@ -1705,7 +1775,11 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
Ok(()) Ok(())
} }
Type::Fn { args, ret, .. } => { Type::Fn {
args,
ret,
alias: _,
} => {
for arg in args { for arg in args {
unify_unbound_type(arg.clone(), own_id, location)?; unify_unbound_type(arg.clone(), own_id, location)?;
} }
@ -1713,7 +1787,7 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
unify_unbound_type(ret.clone(), own_id, location) unify_unbound_type(ret.clone(), own_id, location)
} }
Type::Tuple { elems, .. } => { Type::Tuple { elems, alias: _ } => {
for elem in elems { for elem in elems {
unify_unbound_type(elem.clone(), own_id, location)? unify_unbound_type(elem.clone(), own_id, location)?
} }
@ -1800,9 +1874,9 @@ pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Option<(Spa
} }
pub(super) fn collapse_links(t: Rc<Type>) -> Rc<Type> { pub(super) fn collapse_links(t: Rc<Type>) -> Rc<Type> {
if let Type::Var { tipo, .. } = t.deref() { if let Type::Var { tipo, alias } = t.deref() {
if let TypeVar::Link { tipo } = tipo.borrow().deref() { if let TypeVar::Link { tipo } = tipo.borrow().deref() {
return tipo.clone(); return Type::with_alias(tipo.clone(), alias.clone());
} }
} }
t t
@ -1856,6 +1930,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
Type::App { Type::App {
public, public,
contains_opaque: opaque,
module, module,
name, name,
args, args,
@ -1868,6 +1943,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
public: *public, public: *public,
contains_opaque: *opaque,
module: module.clone(), module: module.clone(),
name: name.clone(), name: name.clone(),
args, args,

View File

@ -259,6 +259,19 @@ You can use '{discard}' and numbers to distinguish between similar names.
name: String, name: String,
}, },
#[error("I caught an opaque type possibly breaking its abstraction boundary.\n")]
#[diagnostic(code("illegal::expect_on_opaque"))]
#[diagnostic(url("https://aiken-lang.org/language-tour/modules#opaque-types"))]
#[diagnostic(help(
"This expression is trying to convert something unknown into an opaque type. An opaque type is a data-type which hides its internal details; usually because it enforces some specific invariant on its internal structure. For example, you might define a {Natural} type that holds an {Integer} but ensures that it never gets negative.\n\nA direct consequence means that it isn't generally possible, nor safe, to turn *any* value into an opaque type. Instead, use the constructors and methods provided for lifting values into that opaque type while ensuring that any structural invariant is checked for.",
Natural = "Natural".if_supports_color(Stdout, |s| s.cyan()),
Integer = "Integer".if_supports_color(Stdout, |s| s.cyan()),
))]
ExpectOnOpaqueType {
#[label("reckless opaque cast")]
location: Span,
},
#[error("I found a type definition that has a function type in it. This is not allowed.\n")] #[error("I found a type definition that has a function type in it. This is not allowed.\n")]
#[diagnostic(code("illegal::function_in_type"))] #[diagnostic(code("illegal::function_in_type"))]
#[diagnostic(help( #[diagnostic(help(
@ -1045,6 +1058,7 @@ impl ExtraData for Error {
| Error::IncorrectTestArity { .. } | Error::IncorrectTestArity { .. }
| Error::GenericLeftAtBoundary { .. } | Error::GenericLeftAtBoundary { .. }
| Error::UnexpectedMultiPatternAssignment { .. } | Error::UnexpectedMultiPatternAssignment { .. }
| Error::ExpectOnOpaqueType { .. }
| Error::ValidatorMustReturnBool { .. } => None, | Error::ValidatorMustReturnBool { .. } => None,
Error::UnknownType { name, .. } Error::UnknownType { name, .. }

View File

@ -211,10 +211,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
match expr { match expr {
UntypedExpr::ErrorTerm { location } => Ok(self.infer_error_term(location)), UntypedExpr::ErrorTerm { location } => Ok(self.infer_error_term(location)),
UntypedExpr::Var { location, name, .. } => self.infer_var(name, location), UntypedExpr::Var { location, name } => self.infer_var(name, location),
UntypedExpr::UInt { UntypedExpr::UInt {
location, value, .. location,
value,
base: _,
} => Ok(self.infer_uint(value, location)), } => Ok(self.infer_uint(value, location)),
UntypedExpr::Sequence { UntypedExpr::Sequence {
@ -222,13 +224,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, location,
} => self.infer_seq(location, expressions), } => self.infer_seq(location, expressions),
UntypedExpr::Tuple { UntypedExpr::Tuple { location, elems } => self.infer_tuple(elems, location),
location, elems, ..
} => self.infer_tuple(elems, location),
UntypedExpr::String { UntypedExpr::String { location, value } => Ok(self.infer_string(value, location)),
location, value, ..
} => Ok(self.infer_string(value, location)),
UntypedExpr::LogicalOpChain { UntypedExpr::LogicalOpChain {
kind, kind,
@ -244,7 +242,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
arguments: args, arguments: args,
body, body,
return_annotation, return_annotation,
..
} => self.infer_fn( } => self.infer_fn(
args, args,
&[], &[],
@ -265,7 +262,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
patterns, patterns,
value, value,
kind, kind,
..
} => { } => {
// at this point due to backpassing rewrites, // at this point due to backpassing rewrites,
// patterns is guaranteed to have one item // patterns is guaranteed to have one item
@ -295,14 +291,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, location,
elements, elements,
tail, tail,
..
} => self.infer_list(elements, tail, location), } => self.infer_list(elements, tail, location),
UntypedExpr::Call { UntypedExpr::Call {
location, location,
fun, fun,
arguments: args, arguments: args,
..
} => self.infer_call(*fun, args, location), } => self.infer_call(*fun, args, location),
UntypedExpr::BinOp { UntypedExpr::BinOp {
@ -310,21 +304,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
name, name,
left, left,
right, right,
..
} => self.infer_binop(name, *left, *right, location), } => self.infer_binop(name, *left, *right, location),
UntypedExpr::FieldAccess { UntypedExpr::FieldAccess {
location, location,
label, label,
container, container,
..
} => self.infer_field_access(*container, label, location), } => self.infer_field_access(*container, label, location),
UntypedExpr::TupleIndex { UntypedExpr::TupleIndex {
location, location,
index, index,
tuple, tuple,
..
} => self.infer_tuple_index(*tuple, index, location), } => self.infer_tuple_index(*tuple, index, location),
UntypedExpr::ByteArray { UntypedExpr::ByteArray {
@ -334,7 +325,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} => self.infer_bytearray(bytes, preferred_format, location), } => self.infer_bytearray(bytes, preferred_format, location),
UntypedExpr::CurvePoint { UntypedExpr::CurvePoint {
location, point, .. location,
point,
preferred_format: _,
} => self.infer_curve_point(*point, location), } => self.infer_curve_point(*point, location),
UntypedExpr::RecordUpdate { UntypedExpr::RecordUpdate {
@ -578,7 +571,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
ValueConstructorVariant::Record { ValueConstructorVariant::Record {
field_map: Some(field_map), field_map: Some(field_map),
constructors_count, constructors_count,
.. name: _,
arity: _,
location: _,
module: _,
} => (field_map, *constructors_count), } => (field_map, *constructors_count),
_ => { _ => {
return Err(Error::RecordUpdateInvalidConstructor { return Err(Error::RecordUpdateInvalidConstructor {
@ -704,7 +700,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Ok(record_access) => Ok(record_access), Ok(record_access) => Ok(record_access),
Err(err) => match container { Err(err) => match container {
UntypedExpr::Var { name, location, .. } => { UntypedExpr::Var { name, location } => {
let module_access = let module_access =
self.infer_module_access(&name, label, &location, access_location); self.infer_module_access(&name, label, &location, access_location);
@ -894,7 +890,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
annotation, annotation,
location, location,
doc, doc,
.. tipo: _,
} = arg; } = arg;
let tipo = annotation let tipo = annotation
@ -934,7 +930,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let value_is_data = value_typ.is_data(); let value_is_data = value_typ.is_data();
// Check that any type annotation is accurate. // Check that any type annotation is accurate.
let pattern = if let Some(ann) = annotation { let ann_typ = if let Some(ann) = annotation {
let ann_typ = self let ann_typ = self
.type_from_annotation(ann) .type_from_annotation(ann)
.map(|t| self.instantiate(t, &mut HashMap::new()))?; .map(|t| self.instantiate(t, &mut HashMap::new()))?;
@ -943,18 +939,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
ann_typ.clone(), ann_typ.clone(),
value_typ.clone(), value_typ.clone(),
typed_value.type_defining_location(), typed_value.type_defining_location(),
(kind.is_let() && ann_typ.is_data()) || (kind.is_expect() && value_is_data), (kind.is_let() && ann_typ.is_data()) || kind.is_expect(),
)?; )?;
value_typ = ann_typ.clone(); value_typ = ann_typ.clone();
// Ensure the pattern matches the type of the value Some(ann_typ)
PatternTyper::new(self.environment, &self.hydrator).unify(
untyped_pattern.clone(),
value_typ.clone(),
Some(ann_typ),
kind.is_let(),
)?
} else { } else {
if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() { if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() {
let ann = Annotation::Constructor { let ann = Annotation::Constructor {
@ -975,15 +965,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
}); });
} }
// Ensure the pattern matches the type of the value None
PatternTyper::new(self.environment, &self.hydrator).unify(
untyped_pattern.clone(),
value_typ.clone(),
None,
kind.is_let(),
)?
}; };
// Ensure the pattern matches the type of the value
let pattern = PatternTyper::new(self.environment, &self.hydrator).unify(
untyped_pattern.clone(),
value_typ.clone(),
ann_typ,
kind.is_let(),
)?;
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an // If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
// error we emit a warning which explains that using `expect` is unnecessary. // error we emit a warning which explains that using `expect` is unnecessary.
match kind { match kind {
@ -1078,7 +1070,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
( (
Type::Fn { Type::Fn {
args: expected_arguments, args: expected_arguments,
.. ret: _,
alias: _,
}, },
UntypedExpr::Fn { UntypedExpr::Fn {
arguments, arguments,
@ -1086,7 +1079,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
return_annotation, return_annotation,
location, location,
fn_style, fn_style,
..
}, },
) if fn_style != FnStyle::Capture && expected_arguments.len() == arguments.len() => { ) if fn_style != FnStyle::Capture && expected_arguments.len() == arguments.len() => {
self.infer_fn( self.infer_fn(
@ -1389,9 +1381,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
base, base,
}), }),
Constant::String { Constant::String { location, value } => Ok(Constant::String { location, value }),
location, value, ..
} => Ok(Constant::String { location, value }),
Constant::ByteArray { Constant::ByteArray {
location, location,
@ -1739,7 +1729,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
value, value,
kind, kind,
patterns, patterns,
..
} = breakpoint } = breakpoint
else { else {
unreachable!("backpass misuse: breakpoint isn't an Assignment ?!"); unreachable!("backpass misuse: breakpoint isn't an Assignment ?!");
@ -1767,7 +1756,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
// in front of the continuation sequence. This is because we do not support patterns in function argument // in front of the continuation sequence. This is because we do not support patterns in function argument
// (which is perhaps something we should support?). // (which is perhaps something we should support?).
match pattern { match pattern {
Pattern::Var { name, .. } | Pattern::Discard { name, .. } if kind.is_let() => { Pattern::Var { name, location: _ } | Pattern::Discard { name, location: _ }
if kind.is_let() =>
{
names.push((name.clone(), annotation)); names.push((name.clone(), annotation));
} }
_ => { _ => {
@ -1797,7 +1788,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} }
match *value { match *value {
UntypedExpr::Call { fun, arguments, .. } => { UntypedExpr::Call {
fun,
arguments,
location: _,
} => {
let mut new_arguments = Vec::new(); let mut new_arguments = Vec::new();
new_arguments.extend(arguments); new_arguments.extend(arguments);
new_arguments.push(CallArg { new_arguments.push(CallArg {
@ -1823,7 +1818,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn_style, fn_style,
ref arguments, ref arguments,
ref return_annotation, ref return_annotation,
.. location: _,
body: _,
} => { } => {
let return_annotation = return_annotation.clone(); let return_annotation = return_annotation.clone();
@ -1882,7 +1878,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
breakpoint = Some(expression); breakpoint = Some(expression);
} }
UntypedExpr::Assignment { UntypedExpr::Assignment {
patterns, location, .. patterns,
location,
value: _,
kind: _,
} if patterns.len() > 1 => { } if patterns.len() > 1 => {
return Err(Error::UnexpectedMultiPatternAssignment { return Err(Error::UnexpectedMultiPatternAssignment {
arrow: patterns arrow: patterns
@ -2003,7 +2002,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let tuple = self.infer(tuple)?; let tuple = self.infer(tuple)?;
let tipo = match *tuple.tipo() { let tipo = match *tuple.tipo() {
Type::Tuple { ref elems, .. } => { Type::Tuple {
ref elems,
alias: _,
} => {
let size = elems.len(); let size = elems.len();
if index >= size { if index >= size {
Err(Error::TupleIndexOutOfBound { Err(Error::TupleIndexOutOfBound {
@ -2332,7 +2334,14 @@ fn assert_assignment(expr: TypedExpr) -> Result<TypedExpr, Error> {
pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Result<(), Error> { pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Result<(), Error> {
match t.deref() { match t.deref() {
Type::App { args, .. } => { Type::App {
args,
name: _,
module: _,
public: _,
contains_opaque: _,
alias: _,
} => {
if t.is_ml_result() { if t.is_ml_result() {
return Err(Error::IllegalTypeInData { return Err(Error::IllegalTypeInData {
tipo: t.clone(), tipo: t.clone(),
@ -2347,7 +2356,7 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Resul
Ok(()) Ok(())
} }
Type::Tuple { elems, .. } => { Type::Tuple { elems, alias: _ } => {
elems elems
.iter() .iter()
.map(|e| ensure_serialisable(false, e.clone(), location)) .map(|e| ensure_serialisable(false, e.clone(), location))
@ -2356,7 +2365,11 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Resul
Ok(()) Ok(())
} }
Type::Fn { args, ret, .. } => { Type::Fn {
args,
ret,
alias: _,
} => {
if !allow_fn { if !allow_fn {
return Err(Error::IllegalTypeInData { return Err(Error::IllegalTypeInData {
tipo: t.clone(), tipo: t.clone(),
@ -2371,10 +2384,14 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Resul
ensure_serialisable(allow_fn, ret.clone(), location) ensure_serialisable(allow_fn, ret.clone(), location)
} }
Type::Var { tipo, .. } => match tipo.borrow().deref() { Type::Var { tipo, alias } => match tipo.borrow().deref() {
TypeVar::Unbound { .. } => Ok(()), TypeVar::Unbound { .. } => Ok(()),
TypeVar::Generic { .. } => Ok(()), TypeVar::Generic { .. } => Ok(()),
TypeVar::Link { tipo } => ensure_serialisable(allow_fn, tipo.clone(), location), TypeVar::Link { tipo } => ensure_serialisable(
allow_fn,
Type::with_alias(tipo.clone(), alias.clone()),
location,
),
}, },
} }
} }

View File

@ -89,6 +89,7 @@ impl UntypedModule {
&self.lines, &self.lines,
tracing, tracing,
)?; )?;
definitions.push(definition); definitions.push(definition);
} }
@ -197,7 +198,8 @@ fn infer_definition(
ArgName::Named { ArgName::Named {
name, name,
is_validator_param, is_validator_param,
.. label: _,
location: _,
} if *is_validator_param => { } if *is_validator_param => {
environment.insert_variable( environment.insert_variable(
name.to_string(), name.to_string(),
@ -383,7 +385,9 @@ fn infer_definition(
.get_mut(&f.name) .get_mut(&f.name)
.expect("Could not find preregistered type for test"); .expect("Could not find preregistered type for test");
if let Type::Fn { if let Type::Fn {
ref ret, ref alias, .. ref ret,
ref alias,
args: _,
} = scope.tipo.as_ref() } = scope.tipo.as_ref()
{ {
scope.tipo = Rc::new(Type::Fn { scope.tipo = Rc::new(Type::Fn {
@ -425,7 +429,11 @@ fn infer_definition(
arguments: match typed_via { arguments: match typed_via {
Some((via, tipo)) => { Some((via, tipo)) => {
let Arg { let Arg {
arg_name, location, .. arg_name,
location,
annotation: _,
doc: _,
tipo: _,
} = typed_f } = typed_f
.arguments .arguments
.first() .first()
@ -457,7 +465,7 @@ fn infer_definition(
alias, alias,
parameters, parameters,
annotation, annotation,
.. tipo: _,
}) => { }) => {
let tipo = environment let tipo = environment
.get_type_constructor(&None, &alias, location) .get_type_constructor(&None, &alias, location)
@ -484,64 +492,66 @@ fn infer_definition(
name, name,
parameters, parameters,
constructors: untyped_constructors, constructors: untyped_constructors,
.. typed_parameters: _,
}) => { }) => {
let constructors = untyped_constructors let constructors = untyped_constructors
.into_iter() .into_iter()
.map( .map(|constructor| {
|RecordConstructor { let preregistered_fn = environment
location, .get_variable(&constructor.name)
name, .expect("Could not find preregistered type for function");
arguments: args,
doc,
sugar,
}| {
let preregistered_fn = environment
.get_variable(&name)
.expect("Could not find preregistered type for function");
let preregistered_type = preregistered_fn.tipo.clone(); let preregistered_type = preregistered_fn.tipo.clone();
let args = if let Some((args_types, _return_type)) = let args = preregistered_type.function_types().map_or(
preregistered_type.function_types() Ok(vec![]),
{ |(args_types, _return_type)| {
args.into_iter() constructor
.arguments
.into_iter()
.zip(&args_types) .zip(&args_types)
.map( .map(|(arg, t)| {
|( if t.is_function() {
RecordConstructorArg { return Err(Error::FunctionTypeInData {
label, location: arg.location,
annotation, });
location, }
doc,
..
},
t,
)| {
RecordConstructorArg {
label,
annotation,
location,
tipo: t.clone(),
doc,
}
},
)
.collect()
} else {
vec![]
};
RecordConstructor { if t.is_ml_result() {
location, return Err(Error::IllegalTypeInData {
name, location: arg.location,
arguments: args, tipo: t.clone(),
doc, });
sugar, }
}
}, if t.contains_opaque() {
) let parent = environment
.collect(); .get_type_constructor_mut(&name, location)?;
Rc::make_mut(&mut parent.tipo).set_opaque(true)
}
Ok(RecordConstructorArg {
label: arg.label,
annotation: arg.annotation,
location: arg.location,
doc: arg.doc,
tipo: t.clone(),
})
})
.collect()
},
)?;
Ok(RecordConstructor {
location: constructor.location,
name: constructor.name,
arguments: args,
doc: constructor.doc,
sugar: constructor.sugar,
})
})
.collect::<Result<_, Error>>()?;
let typed_parameters = environment let typed_parameters = environment
.get_type_constructor(&None, &name, location) .get_type_constructor(&None, &name, location)
@ -561,7 +571,14 @@ fn infer_definition(
}; };
for constr in &typed_data.constructors { for constr in &typed_data.constructors {
for RecordConstructorArg { tipo, location, .. } in &constr.arguments { for RecordConstructorArg {
tipo,
location,
doc: _,
label: _,
annotation: _,
} in &constr.arguments
{
if tipo.is_function() { if tipo.is_function() {
return Err(Error::FunctionTypeInData { return Err(Error::FunctionTypeInData {
location: *location, location: *location,
@ -585,7 +602,7 @@ fn infer_definition(
module, module,
as_name, as_name,
unqualified, unqualified,
.. package: _,
}) => { }) => {
let name = module.join("/"); let name = module.join("/");
@ -616,7 +633,7 @@ fn infer_definition(
annotation, annotation,
public, public,
value, value,
.. tipo: _,
}) => { }) => {
let typed_expr = let typed_expr =
ExprTyper::new(environment, lines, tracing).infer_const(&annotation, *value)?; ExprTyper::new(environment, lines, tracing).infer_const(&annotation, *value)?;
@ -672,7 +689,7 @@ fn infer_function(
return_annotation, return_annotation,
end_position, end_position,
can_error, can_error,
.. return_type: _,
} = f; } = f;
let preregistered_fn = environment let preregistered_fn = environment
@ -773,9 +790,18 @@ fn infer_fuzzer(
}; };
match tipo.borrow() { match tipo.borrow() {
Type::Fn { ret, .. } => match ret.borrow() { Type::Fn {
ret,
args: _,
alias: _,
} => match ret.borrow() {
Type::App { Type::App {
module, name, args, .. module,
name,
args,
public: _,
contains_opaque: _,
alias: _,
} if module.is_empty() && name == "Option" && args.len() == 1 => { } if module.is_empty() && name == "Option" && args.len() == 1 => {
match args.first().expect("args.len() == 1").borrow() { match args.first().expect("args.len() == 1").borrow() {
Type::Tuple { elems, .. } if elems.len() == 2 => { Type::Tuple { elems, .. } if elems.len() == 2 => {
@ -805,10 +831,13 @@ fn infer_fuzzer(
_ => Err(could_not_unify()), _ => Err(could_not_unify()),
}, },
Type::Var { tipo, .. } => match &*tipo.deref().borrow() { Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
TypeVar::Link { tipo } => { TypeVar::Link { tipo } => infer_fuzzer(
infer_fuzzer(environment, expected_inner_type, tipo, location) environment,
} expected_inner_type,
&Type::with_alias(tipo.clone(), alias.clone()),
location,
),
_ => Err(Error::GenericLeftAtBoundary { _ => Err(Error::GenericLeftAtBoundary {
location: *location, location: *location,
}), }),
@ -821,7 +850,12 @@ fn infer_fuzzer(
fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> { fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
match tipo { match tipo {
Type::App { Type::App {
name, module, args, .. name,
module,
args,
public: _,
contains_opaque: _,
alias: _,
} => { } => {
let arguments = args let arguments = args
.iter() .iter()
@ -839,7 +873,7 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
}) })
} }
Type::Tuple { elems, .. } => { Type::Tuple { elems, alias: _ } => {
let elems = elems let elems = elems
.iter() .iter()
.map(|arg| annotate_fuzzer(arg, location)) .map(|arg| annotate_fuzzer(arg, location))
@ -850,7 +884,7 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
}) })
} }
Type::Var { tipo, .. } => match &*tipo.deref().borrow() { Type::Var { tipo, alias: _ } => match &*tipo.deref().borrow() {
TypeVar::Link { tipo } => annotate_fuzzer(tipo, location), TypeVar::Link { tipo } => annotate_fuzzer(tipo, location),
_ => Err(Error::GenericLeftAtBoundary { _ => Err(Error::GenericLeftAtBoundary {
location: *location, location: *location,

View File

@ -141,11 +141,11 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
pattern: UntypedPattern, pattern: UntypedPattern,
tipo: Rc<Type>, tipo: Rc<Type>,
ann_type: Option<Rc<Type>>, ann_type: Option<Rc<Type>>,
is_assignment: bool, is_let: bool,
) -> Result<TypedPattern, Error> { ) -> Result<TypedPattern, Error> {
match pattern { match pattern {
Pattern::Discard { name, location } => { Pattern::Discard { name, location } => {
if is_assignment { if is_let {
// Register declaration for the unused variable detection // Register declaration for the unused variable detection
self.environment self.environment
.warnings .warnings
@ -154,6 +154,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
location, location,
}); });
}; };
Ok(Pattern::Discard { name, location }) Ok(Pattern::Discard { name, location })
} }
@ -202,7 +203,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
location, location,
elements, elements,
tail, tail,
} => match tipo.get_app_args(true, "", "List", 1, self.environment) { } => match tipo.get_app_args(true, false, "", "List", 1, self.environment) {
Some(args) => { Some(args) => {
let tipo = args let tipo = args
.first() .first()

View File

@ -368,6 +368,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Int".to_string(), name: "Int".to_string(),
public: true, public: true,
contains_opaque: false,
args: vec![], args: vec![],
alias: None alias: None
}, },
@ -378,12 +379,14 @@ mod tests {
module: "".to_string(), module: "".to_string(),
name: "Pair".to_string(), name: "Pair".to_string(),
public: true, public: true,
contains_opaque: false,
alias: None, alias: None,
args: vec![ args: vec![
Rc::new(Type::App { Rc::new(Type::App {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Int".to_string(), name: "Int".to_string(),
public: true, public: true,
contains_opaque: false,
args: vec![], args: vec![],
alias: None alias: None
}), }),
@ -391,6 +394,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Bool".to_string(), name: "Bool".to_string(),
public: true, public: true,
contains_opaque: false,
args: vec![], args: vec![],
alias: None alias: None
}), }),
@ -406,6 +410,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Int".to_string(), name: "Int".to_string(),
public: true, public: true,
contains_opaque: false,
alias: None, alias: None,
}), }),
Rc::new(Type::App { Rc::new(Type::App {
@ -413,6 +418,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Bool".to_string(), name: "Bool".to_string(),
public: true, public: true,
contains_opaque: false,
alias: None, alias: None,
}), }),
], ],
@ -421,6 +427,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Bool".to_string(), name: "Bool".to_string(),
public: true, public: true,
contains_opaque: false,
alias: None, alias: None,
}), }),
alias: None, alias: None,
@ -437,6 +444,7 @@ mod tests {
module: "whatever".to_string(), module: "whatever".to_string(),
name: "Int".to_string(), name: "Int".to_string(),
public: true, public: true,
contains_opaque: false,
}), }),
})), })),
}, },
@ -479,6 +487,7 @@ mod tests {
Type::Fn { Type::Fn {
args: vec![Rc::new(Type::App { args: vec![Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "PRNG".to_string(), name: "PRNG".to_string(),
args: vec![], args: vec![],
@ -486,12 +495,14 @@ mod tests {
})], })],
ret: Rc::new(Type::App { ret: Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "Option".to_string(), name: "Option".to_string(),
args: vec![Rc::new(Type::Tuple { args: vec![Rc::new(Type::Tuple {
elems: vec![ elems: vec![
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "PRNG".to_string(), name: "PRNG".to_string(),
args: vec![], args: vec![],
@ -499,6 +510,7 @@ mod tests {
}), }),
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "Bool".to_string(), name: "Bool".to_string(),
args: vec![], args: vec![],
@ -549,6 +561,7 @@ mod tests {
Type::Fn { Type::Fn {
args: vec![Rc::new(Type::App { args: vec![Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "PRNG".to_string(), name: "PRNG".to_string(),
args: vec![], args: vec![],
@ -556,12 +569,14 @@ mod tests {
})], })],
ret: Rc::new(Type::App { ret: Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "Option".to_string(), name: "Option".to_string(),
args: vec![Rc::new(Type::Tuple { args: vec![Rc::new(Type::Tuple {
elems: vec![ elems: vec![
Rc::new(Type::App { Rc::new(Type::App {
public: true, public: true,
contains_opaque: false,
module: "".to_string(), module: "".to_string(),
name: "PRNG".to_string(), name: "PRNG".to_string(),
args: vec![], args: vec![],

View File

@ -8,6 +8,7 @@ Schema {
breadcrumbs: [ breadcrumbs: [
App { App {
public: true, public: true,
contains_opaque: true,
module: "test_module", module: "test_module",
name: "Rational", name: "Rational",
args: [], args: [],

View File

@ -8,6 +8,7 @@ Schema {
breadcrumbs: [ breadcrumbs: [
App { App {
public: true, public: true,
contains_opaque: true,
module: "test_module", module: "test_module",
name: "Dict", name: "Dict",
args: [ args: [
@ -16,6 +17,7 @@ Schema {
value: Link { value: Link {
tipo: App { tipo: App {
public: false, public: false,
contains_opaque: false,
module: "test_module", module: "test_module",
name: "UUID", name: "UUID",
args: [], args: [],
@ -30,6 +32,7 @@ Schema {
value: Link { value: Link {
tipo: App { tipo: App {
public: true, public: true,
contains_opaque: false,
module: "", module: "",
name: "Int", name: "Int",
args: [], args: [],