Merge pull request #875 from aiken-lang/rvcas/expect_opaque
block expects on opaque types
This commit is contained in:
commit
3f254dbe6b
|
@ -1267,6 +1267,7 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
|
|||
pub fn int() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: INT.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
|
@ -1277,6 +1278,7 @@ pub fn int() -> Rc<Type> {
|
|||
pub fn data() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: DATA.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
|
@ -1288,6 +1290,7 @@ pub fn byte_array() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: BYTE_ARRAY.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1297,6 +1300,7 @@ pub fn byte_array() -> Rc<Type> {
|
|||
pub fn g1_element() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: G1_ELEMENT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1307,6 +1311,7 @@ pub fn g1_element() -> Rc<Type> {
|
|||
pub fn g2_element() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: G2_ELEMENT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1317,6 +1322,7 @@ pub fn g2_element() -> Rc<Type> {
|
|||
pub fn miller_loop_result() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: MILLER_LOOP_RESULT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1332,6 +1338,7 @@ pub fn bool() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: BOOL.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1342,6 +1349,7 @@ pub fn prng() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: PRNG.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1355,6 +1363,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
|||
name: "PRNG".to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
|
||||
Rc::new(Type::Fn {
|
||||
args: vec![prng()],
|
||||
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> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: LIST.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![t],
|
||||
|
@ -1402,6 +1412,7 @@ pub fn string() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: STRING.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1412,6 +1423,7 @@ pub fn void() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: VOID.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1421,6 +1433,7 @@ pub fn void() -> Rc<Type> {
|
|||
pub fn option(a: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: OPTION.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![a],
|
||||
|
@ -1431,6 +1444,7 @@ pub fn option(a: Rc<Type>) -> Rc<Type> {
|
|||
pub fn ordering() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: ORDERING.to_string(),
|
||||
module: "".to_string(),
|
||||
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> {
|
||||
let tipo = Rc::new(RefCell::new(TypeVar::Generic { id }));
|
||||
|
||||
Rc::new(Type::Var { tipo, alias: None })
|
||||
}
|
||||
|
||||
pub fn unbound_var(id: u64) -> Rc<Type> {
|
||||
let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id }));
|
||||
|
||||
Rc::new(Type::Var { tipo, alias: None })
|
||||
}
|
||||
|
||||
pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: REDEEMER_WRAPPER.to_string(),
|
||||
args: vec![redeemer],
|
||||
|
|
|
@ -16,6 +16,7 @@ fn parse(source_code: &str) -> UntypedModule {
|
|||
|
||||
fn check_module(
|
||||
ast: UntypedModule,
|
||||
extra: Vec<(String, UntypedModule)>,
|
||||
kind: ModuleKind,
|
||||
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||
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/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(
|
||||
&id_gen,
|
||||
kind,
|
||||
|
@ -41,13 +57,20 @@ fn check_module(
|
|||
}
|
||||
|
||||
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(
|
||||
ast: UntypedModule,
|
||||
) -> Result<(Vec<Warning>, TypedModule), (Vec<Warning>, Error)> {
|
||||
check_module(ast, ModuleKind::Validator)
|
||||
check_module(ast, Vec::new(), ModuleKind::Validator)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1761,7 +1784,7 @@ fn backpassing_type_annotation() {
|
|||
(Foo(1), inputs)
|
||||
}
|
||||
[input, ..remaining_inputs] -> {
|
||||
|
||||
|
||||
callback(input)(
|
||||
fn(foo) {
|
||||
transition_fold4(
|
||||
|
@ -1779,7 +1802,7 @@ fn backpassing_type_annotation() {
|
|||
transition_fold4(
|
||||
x,
|
||||
)
|
||||
|
||||
|
||||
fn(g){
|
||||
g(if input.foo == 1{
|
||||
1
|
||||
|
@ -1787,7 +1810,175 @@ fn backpassing_type_annotation() {
|
|||
2
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
"#;
|
||||
|
||||
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
|
||||
}
|
||||
"#;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ pub enum Type {
|
|||
///
|
||||
App {
|
||||
public: bool,
|
||||
contains_opaque: bool,
|
||||
module: String,
|
||||
name: String,
|
||||
args: Vec<Rc<Type>>,
|
||||
|
@ -80,19 +81,22 @@ impl PartialEq for Type {
|
|||
module,
|
||||
name,
|
||||
args,
|
||||
..
|
||||
contains_opaque: opaque,
|
||||
alias: _,
|
||||
} => {
|
||||
if let Type::App {
|
||||
public: public2,
|
||||
module: module2,
|
||||
name: name2,
|
||||
args: args2,
|
||||
..
|
||||
contains_opaque: opaque2,
|
||||
alias: _,
|
||||
} = other
|
||||
{
|
||||
name == name2
|
||||
&& module == module2
|
||||
&& public == public2
|
||||
&& opaque == opaque2
|
||||
&& args.iter().zip(args2).all(|(left, right)| left == right)
|
||||
} else {
|
||||
false
|
||||
|
@ -103,7 +107,7 @@ impl PartialEq for Type {
|
|||
if let Type::Fn {
|
||||
args: args2,
|
||||
ret: ret2,
|
||||
..
|
||||
alias: _,
|
||||
} = other
|
||||
{
|
||||
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 {
|
||||
elems.iter().zip(elems2).all(|(left, right)| left == right)
|
||||
} else {
|
||||
|
@ -120,8 +124,12 @@ impl PartialEq for Type {
|
|||
}
|
||||
}
|
||||
|
||||
Type::Var { tipo, .. } => {
|
||||
if let Type::Var { tipo: tipo2, .. } = other {
|
||||
Type::Var { tipo, alias: _ } => {
|
||||
if let Type::Var {
|
||||
tipo: tipo2,
|
||||
alias: _,
|
||||
} = other
|
||||
{
|
||||
tipo == tipo2
|
||||
} else {
|
||||
false
|
||||
|
@ -152,20 +160,26 @@ impl Type {
|
|||
Rc::new(match self {
|
||||
Type::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
..
|
||||
alias: _,
|
||||
} => Type::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
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 },
|
||||
Type::Fn {
|
||||
args,
|
||||
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 {
|
||||
Type::Fn { ret, .. } => ret.is_result(),
|
||||
_ => false,
|
||||
Type::Var { tipo, .. } => tipo.borrow().is_or_holds_opaque(),
|
||||
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 {
|
||||
matches!(self, Self::App { name, module, .. } if "Result" == name && module.is_empty())
|
||||
pub fn set_opaque(&mut self, opaque: bool) {
|
||||
match self {
|
||||
Type::App {
|
||||
contains_opaque, ..
|
||||
} => {
|
||||
*contains_opaque = opaque;
|
||||
}
|
||||
Type::Fn { .. } | Type::Var { .. } | Type::Tuple { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unbound(&self) -> bool {
|
||||
|
@ -459,6 +486,7 @@ impl Type {
|
|||
pub fn get_app_args(
|
||||
&self,
|
||||
public: bool,
|
||||
opaque: bool,
|
||||
module: &str,
|
||||
name: &str,
|
||||
arity: usize,
|
||||
|
@ -481,7 +509,7 @@ impl Type {
|
|||
Self::Var { tipo, alias } => {
|
||||
let args: Vec<_> = match tipo.borrow().deref() {
|
||||
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 { .. } => {
|
||||
|
@ -496,6 +524,7 @@ impl Type {
|
|||
*tipo.borrow_mut() = TypeVar::Link {
|
||||
tipo: Rc::new(Self::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
name: name.to_string(),
|
||||
module: module.to_owned(),
|
||||
args: args.clone(),
|
||||
|
@ -650,6 +679,7 @@ pub fn convert_opaque_type(
|
|||
match t.as_ref() {
|
||||
Type::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
|
@ -662,6 +692,7 @@ pub fn convert_opaque_type(
|
|||
}
|
||||
Type::App {
|
||||
public: *public,
|
||||
contains_opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
args: new_args,
|
||||
|
@ -736,6 +767,7 @@ pub fn find_and_replace_generics(
|
|||
Type::App {
|
||||
args,
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
module,
|
||||
name,
|
||||
alias,
|
||||
|
@ -748,6 +780,7 @@ pub fn find_and_replace_generics(
|
|||
let t = Type::App {
|
||||
args: new_args,
|
||||
public: *public,
|
||||
contains_opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
alias: alias.clone(),
|
||||
|
@ -829,6 +862,13 @@ impl TypeVar {
|
|||
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 {
|
||||
match self {
|
||||
Self::Link { tipo } => tipo.is_void(),
|
||||
|
|
|
@ -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 {
|
||||
Err(Error::IncorrectFunctionCallArity {
|
||||
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.
|
||||
pub fn get_type_constructor(
|
||||
&mut self,
|
||||
|
@ -546,6 +570,7 @@ impl<'a> Environment<'a> {
|
|||
match t.deref() {
|
||||
Type::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
name,
|
||||
module,
|
||||
args,
|
||||
|
@ -555,8 +580,10 @@ impl<'a> Environment<'a> {
|
|||
.iter()
|
||||
.map(|t| self.instantiate(t.clone(), ids, hydrator))
|
||||
.collect();
|
||||
|
||||
Rc::new(Type::App {
|
||||
public: *public,
|
||||
contains_opaque: *opaque,
|
||||
name: name.clone(),
|
||||
module: module.clone(),
|
||||
alias: alias.clone(),
|
||||
|
@ -727,7 +754,7 @@ impl<'a> Environment<'a> {
|
|||
as_name,
|
||||
unqualified,
|
||||
location,
|
||||
..
|
||||
package: _,
|
||||
}) => {
|
||||
let name = module.join("/");
|
||||
|
||||
|
@ -762,7 +789,6 @@ impl<'a> Environment<'a> {
|
|||
name,
|
||||
location,
|
||||
as_name,
|
||||
..
|
||||
} in unqualified
|
||||
{
|
||||
let mut type_imported = false;
|
||||
|
@ -983,10 +1009,12 @@ impl<'a> Environment<'a> {
|
|||
Definition::DataType(DataType {
|
||||
name,
|
||||
public,
|
||||
opaque,
|
||||
parameters,
|
||||
location,
|
||||
constructors,
|
||||
..
|
||||
doc: _,
|
||||
typed_parameters: _,
|
||||
}) => {
|
||||
assert_unique_type_name(names, name, location)?;
|
||||
|
||||
|
@ -997,6 +1025,7 @@ impl<'a> Environment<'a> {
|
|||
|
||||
let tipo = Rc::new(Type::App {
|
||||
public: *public,
|
||||
contains_opaque: *opaque,
|
||||
module: module.to_owned(),
|
||||
name: name.clone(),
|
||||
args: parameters.clone(),
|
||||
|
@ -1032,7 +1061,8 @@ impl<'a> Environment<'a> {
|
|||
parameters: args,
|
||||
alias: name,
|
||||
annotation: resolved_type,
|
||||
..
|
||||
doc: _,
|
||||
tipo: _,
|
||||
}) => {
|
||||
assert_unique_type_name(names, name, location)?;
|
||||
|
||||
|
@ -1173,7 +1203,9 @@ impl<'a> Environment<'a> {
|
|||
fun,
|
||||
other_fun,
|
||||
params,
|
||||
..
|
||||
doc: _,
|
||||
location: _,
|
||||
end_position: _,
|
||||
}) if kind.is_validator() => {
|
||||
let default_annotation = |mut arg: UntypedArg| {
|
||||
if arg.annotation.is_none() {
|
||||
|
@ -1251,7 +1283,10 @@ impl<'a> Environment<'a> {
|
|||
opaque,
|
||||
name,
|
||||
constructors,
|
||||
..
|
||||
doc: _,
|
||||
location: _,
|
||||
parameters: _,
|
||||
typed_parameters: _,
|
||||
}) => {
|
||||
let mut hydrator = hydrators
|
||||
.remove(name)
|
||||
|
@ -1294,7 +1329,8 @@ impl<'a> Environment<'a> {
|
|||
label,
|
||||
annotation,
|
||||
location,
|
||||
..
|
||||
tipo: _,
|
||||
doc: _,
|
||||
},
|
||||
) in constructor.arguments.iter().enumerate()
|
||||
{
|
||||
|
@ -1379,18 +1415,28 @@ impl<'a> Environment<'a> {
|
|||
&& !(t1.is_function() || t2.is_function())
|
||||
&& !(t1.is_generic() || t2.is_generic())
|
||||
&& !(t1.is_string() || t2.is_string())
|
||||
&& !(t1.contains_opaque() || t2.contains_opaque())
|
||||
{
|
||||
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.
|
||||
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, alias } = t2.deref() {
|
||||
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
|
||||
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 {
|
||||
Unify(Rc<Type>),
|
||||
CouldNotUnify,
|
||||
|
@ -1398,7 +1444,9 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
|
||||
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 } => {
|
||||
unify_unbound_type(t2.clone(), *id, location)?;
|
||||
|
@ -1406,7 +1454,7 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
|
||||
TypeVar::Generic { id } => {
|
||||
if let Type::Var { tipo, .. } = t2.deref() {
|
||||
if let Type::Var { tipo, alias: _ } = t2.deref() {
|
||||
if tipo.borrow().is_unbound() {
|
||||
*tipo.borrow_mut() = TypeVar::Generic { id: *id };
|
||||
return Ok(());
|
||||
|
@ -1446,13 +1494,17 @@ impl<'a> Environment<'a> {
|
|||
module: m1,
|
||||
name: n1,
|
||||
args: args1,
|
||||
..
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
},
|
||||
Type::App {
|
||||
module: m2,
|
||||
name: n2,
|
||||
args: args2,
|
||||
..
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
},
|
||||
) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => {
|
||||
for (a, b) in args1.iter().zip(args2) {
|
||||
|
@ -1465,9 +1517,16 @@ impl<'a> Environment<'a> {
|
|||
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) {
|
||||
unify_enclosed_type(
|
||||
t1.clone(),
|
||||
|
@ -1482,12 +1541,12 @@ impl<'a> Environment<'a> {
|
|||
Type::Fn {
|
||||
args: args1,
|
||||
ret: retrn1,
|
||||
..
|
||||
alias: _,
|
||||
},
|
||||
Type::Fn {
|
||||
args: args2,
|
||||
ret: retrn2,
|
||||
..
|
||||
alias: _,
|
||||
},
|
||||
) if args1.len() == args2.len() => {
|
||||
for (a, b) in args1.iter().zip(args2) {
|
||||
|
@ -1673,10 +1732,14 @@ pub enum EntityKind {
|
|||
/// could cause naively-implemented type checking to diverge.
|
||||
/// While traversing the type tree.
|
||||
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() {
|
||||
TypeVar::Link { tipo, .. } => {
|
||||
return unify_unbound_type(tipo.clone(), own_id, location);
|
||||
TypeVar::Link { tipo } => {
|
||||
return unify_unbound_type(
|
||||
Type::with_alias(tipo.clone(), alias.clone()),
|
||||
own_id,
|
||||
location,
|
||||
);
|
||||
}
|
||||
|
||||
TypeVar::Unbound { id } => {
|
||||
|
@ -1697,7 +1760,14 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
|
|||
}
|
||||
|
||||
match tipo.deref() {
|
||||
Type::App { args, .. } => {
|
||||
Type::App {
|
||||
args,
|
||||
module: _,
|
||||
name: _,
|
||||
public: _,
|
||||
alias: _,
|
||||
contains_opaque: _,
|
||||
} => {
|
||||
for arg in args {
|
||||
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(())
|
||||
}
|
||||
|
||||
Type::Fn { args, ret, .. } => {
|
||||
Type::Fn {
|
||||
args,
|
||||
ret,
|
||||
alias: _,
|
||||
} => {
|
||||
for arg in args {
|
||||
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)
|
||||
}
|
||||
|
||||
Type::Tuple { elems, .. } => {
|
||||
Type::Tuple { elems, alias: _ } => {
|
||||
for elem in elems {
|
||||
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> {
|
||||
if let Type::Var { tipo, .. } = t.deref() {
|
||||
if let Type::Var { tipo, alias } = t.deref() {
|
||||
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
|
||||
return tipo.clone();
|
||||
return Type::with_alias(tipo.clone(), alias.clone());
|
||||
}
|
||||
}
|
||||
t
|
||||
|
@ -1856,6 +1930,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
|
|||
|
||||
Type::App {
|
||||
public,
|
||||
contains_opaque: opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
|
@ -1868,6 +1943,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
|
|||
|
||||
Rc::new(Type::App {
|
||||
public: *public,
|
||||
contains_opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
args,
|
||||
|
|
|
@ -259,6 +259,19 @@ You can use '{discard}' and numbers to distinguish between similar names.
|
|||
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")]
|
||||
#[diagnostic(code("illegal::function_in_type"))]
|
||||
#[diagnostic(help(
|
||||
|
@ -1045,6 +1058,7 @@ impl ExtraData for Error {
|
|||
| Error::IncorrectTestArity { .. }
|
||||
| Error::GenericLeftAtBoundary { .. }
|
||||
| Error::UnexpectedMultiPatternAssignment { .. }
|
||||
| Error::ExpectOnOpaqueType { .. }
|
||||
| Error::ValidatorMustReturnBool { .. } => None,
|
||||
|
||||
Error::UnknownType { name, .. }
|
||||
|
|
|
@ -211,10 +211,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
match expr {
|
||||
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 {
|
||||
location, value, ..
|
||||
location,
|
||||
value,
|
||||
base: _,
|
||||
} => Ok(self.infer_uint(value, location)),
|
||||
|
||||
UntypedExpr::Sequence {
|
||||
|
@ -222,13 +224,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
location,
|
||||
} => self.infer_seq(location, expressions),
|
||||
|
||||
UntypedExpr::Tuple {
|
||||
location, elems, ..
|
||||
} => self.infer_tuple(elems, location),
|
||||
UntypedExpr::Tuple { location, elems } => self.infer_tuple(elems, location),
|
||||
|
||||
UntypedExpr::String {
|
||||
location, value, ..
|
||||
} => Ok(self.infer_string(value, location)),
|
||||
UntypedExpr::String { location, value } => Ok(self.infer_string(value, location)),
|
||||
|
||||
UntypedExpr::LogicalOpChain {
|
||||
kind,
|
||||
|
@ -244,7 +242,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
arguments: args,
|
||||
body,
|
||||
return_annotation,
|
||||
..
|
||||
} => self.infer_fn(
|
||||
args,
|
||||
&[],
|
||||
|
@ -265,7 +262,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
patterns,
|
||||
value,
|
||||
kind,
|
||||
..
|
||||
} => {
|
||||
// at this point due to backpassing rewrites,
|
||||
// patterns is guaranteed to have one item
|
||||
|
@ -295,14 +291,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
location,
|
||||
elements,
|
||||
tail,
|
||||
..
|
||||
} => self.infer_list(elements, tail, location),
|
||||
|
||||
UntypedExpr::Call {
|
||||
location,
|
||||
fun,
|
||||
arguments: args,
|
||||
..
|
||||
} => self.infer_call(*fun, args, location),
|
||||
|
||||
UntypedExpr::BinOp {
|
||||
|
@ -310,21 +304,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
name,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => self.infer_binop(name, *left, *right, location),
|
||||
|
||||
UntypedExpr::FieldAccess {
|
||||
location,
|
||||
label,
|
||||
container,
|
||||
..
|
||||
} => self.infer_field_access(*container, label, location),
|
||||
|
||||
UntypedExpr::TupleIndex {
|
||||
location,
|
||||
index,
|
||||
tuple,
|
||||
..
|
||||
} => self.infer_tuple_index(*tuple, index, location),
|
||||
|
||||
UntypedExpr::ByteArray {
|
||||
|
@ -334,7 +325,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
} => self.infer_bytearray(bytes, preferred_format, location),
|
||||
|
||||
UntypedExpr::CurvePoint {
|
||||
location, point, ..
|
||||
location,
|
||||
point,
|
||||
preferred_format: _,
|
||||
} => self.infer_curve_point(*point, location),
|
||||
|
||||
UntypedExpr::RecordUpdate {
|
||||
|
@ -578,7 +571,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
ValueConstructorVariant::Record {
|
||||
field_map: Some(field_map),
|
||||
constructors_count,
|
||||
..
|
||||
name: _,
|
||||
arity: _,
|
||||
location: _,
|
||||
module: _,
|
||||
} => (field_map, *constructors_count),
|
||||
_ => {
|
||||
return Err(Error::RecordUpdateInvalidConstructor {
|
||||
|
@ -704,7 +700,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
Ok(record_access) => Ok(record_access),
|
||||
|
||||
Err(err) => match container {
|
||||
UntypedExpr::Var { name, location, .. } => {
|
||||
UntypedExpr::Var { name, location } => {
|
||||
let module_access =
|
||||
self.infer_module_access(&name, label, &location, access_location);
|
||||
|
||||
|
@ -894,7 +890,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
annotation,
|
||||
location,
|
||||
doc,
|
||||
..
|
||||
tipo: _,
|
||||
} = arg;
|
||||
|
||||
let tipo = annotation
|
||||
|
@ -934,7 +930,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
let value_is_data = value_typ.is_data();
|
||||
|
||||
// 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
|
||||
.type_from_annotation(ann)
|
||||
.map(|t| self.instantiate(t, &mut HashMap::new()))?;
|
||||
|
@ -943,18 +939,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
ann_typ.clone(),
|
||||
value_typ.clone(),
|
||||
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();
|
||||
|
||||
// Ensure the pattern matches the type of the value
|
||||
PatternTyper::new(self.environment, &self.hydrator).unify(
|
||||
untyped_pattern.clone(),
|
||||
value_typ.clone(),
|
||||
Some(ann_typ),
|
||||
kind.is_let(),
|
||||
)?
|
||||
Some(ann_typ)
|
||||
} else {
|
||||
if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() {
|
||||
let ann = Annotation::Constructor {
|
||||
|
@ -975,15 +965,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
});
|
||||
}
|
||||
|
||||
// Ensure the pattern matches the type of the value
|
||||
PatternTyper::new(self.environment, &self.hydrator).unify(
|
||||
untyped_pattern.clone(),
|
||||
value_typ.clone(),
|
||||
None,
|
||||
kind.is_let(),
|
||||
)?
|
||||
None
|
||||
};
|
||||
|
||||
// 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
|
||||
// error we emit a warning which explains that using `expect` is unnecessary.
|
||||
match kind {
|
||||
|
@ -1078,7 +1070,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
(
|
||||
Type::Fn {
|
||||
args: expected_arguments,
|
||||
..
|
||||
ret: _,
|
||||
alias: _,
|
||||
},
|
||||
UntypedExpr::Fn {
|
||||
arguments,
|
||||
|
@ -1086,7 +1079,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
return_annotation,
|
||||
location,
|
||||
fn_style,
|
||||
..
|
||||
},
|
||||
) if fn_style != FnStyle::Capture && expected_arguments.len() == arguments.len() => {
|
||||
self.infer_fn(
|
||||
|
@ -1389,9 +1381,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
base,
|
||||
}),
|
||||
|
||||
Constant::String {
|
||||
location, value, ..
|
||||
} => Ok(Constant::String { location, value }),
|
||||
Constant::String { location, value } => Ok(Constant::String { location, value }),
|
||||
|
||||
Constant::ByteArray {
|
||||
location,
|
||||
|
@ -1739,7 +1729,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
value,
|
||||
kind,
|
||||
patterns,
|
||||
..
|
||||
} = breakpoint
|
||||
else {
|
||||
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
|
||||
// (which is perhaps something we should support?).
|
||||
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));
|
||||
}
|
||||
_ => {
|
||||
|
@ -1797,7 +1788,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
}
|
||||
|
||||
match *value {
|
||||
UntypedExpr::Call { fun, arguments, .. } => {
|
||||
UntypedExpr::Call {
|
||||
fun,
|
||||
arguments,
|
||||
location: _,
|
||||
} => {
|
||||
let mut new_arguments = Vec::new();
|
||||
new_arguments.extend(arguments);
|
||||
new_arguments.push(CallArg {
|
||||
|
@ -1823,7 +1818,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
fn_style,
|
||||
ref arguments,
|
||||
ref return_annotation,
|
||||
..
|
||||
location: _,
|
||||
body: _,
|
||||
} => {
|
||||
let return_annotation = return_annotation.clone();
|
||||
|
||||
|
@ -1882,7 +1878,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
breakpoint = Some(expression);
|
||||
}
|
||||
UntypedExpr::Assignment {
|
||||
patterns, location, ..
|
||||
patterns,
|
||||
location,
|
||||
value: _,
|
||||
kind: _,
|
||||
} if patterns.len() > 1 => {
|
||||
return Err(Error::UnexpectedMultiPatternAssignment {
|
||||
arrow: patterns
|
||||
|
@ -2003,7 +2002,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
let tuple = self.infer(tuple)?;
|
||||
|
||||
let tipo = match *tuple.tipo() {
|
||||
Type::Tuple { ref elems, .. } => {
|
||||
Type::Tuple {
|
||||
ref elems,
|
||||
alias: _,
|
||||
} => {
|
||||
let size = elems.len();
|
||||
if index >= size {
|
||||
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> {
|
||||
match t.deref() {
|
||||
Type::App { args, .. } => {
|
||||
Type::App {
|
||||
args,
|
||||
name: _,
|
||||
module: _,
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
} => {
|
||||
if t.is_ml_result() {
|
||||
return Err(Error::IllegalTypeInData {
|
||||
tipo: t.clone(),
|
||||
|
@ -2347,7 +2356,7 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Resul
|
|||
Ok(())
|
||||
}
|
||||
|
||||
Type::Tuple { elems, .. } => {
|
||||
Type::Tuple { elems, alias: _ } => {
|
||||
elems
|
||||
.iter()
|
||||
.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(())
|
||||
}
|
||||
|
||||
Type::Fn { args, ret, .. } => {
|
||||
Type::Fn {
|
||||
args,
|
||||
ret,
|
||||
alias: _,
|
||||
} => {
|
||||
if !allow_fn {
|
||||
return Err(Error::IllegalTypeInData {
|
||||
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)
|
||||
}
|
||||
|
||||
Type::Var { tipo, .. } => match tipo.borrow().deref() {
|
||||
Type::Var { tipo, alias } => match tipo.borrow().deref() {
|
||||
TypeVar::Unbound { .. } => 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,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ impl UntypedModule {
|
|||
&self.lines,
|
||||
tracing,
|
||||
)?;
|
||||
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,8 @@ fn infer_definition(
|
|||
ArgName::Named {
|
||||
name,
|
||||
is_validator_param,
|
||||
..
|
||||
label: _,
|
||||
location: _,
|
||||
} if *is_validator_param => {
|
||||
environment.insert_variable(
|
||||
name.to_string(),
|
||||
|
@ -383,7 +385,9 @@ fn infer_definition(
|
|||
.get_mut(&f.name)
|
||||
.expect("Could not find preregistered type for test");
|
||||
if let Type::Fn {
|
||||
ref ret, ref alias, ..
|
||||
ref ret,
|
||||
ref alias,
|
||||
args: _,
|
||||
} = scope.tipo.as_ref()
|
||||
{
|
||||
scope.tipo = Rc::new(Type::Fn {
|
||||
|
@ -425,7 +429,11 @@ fn infer_definition(
|
|||
arguments: match typed_via {
|
||||
Some((via, tipo)) => {
|
||||
let Arg {
|
||||
arg_name, location, ..
|
||||
arg_name,
|
||||
location,
|
||||
annotation: _,
|
||||
doc: _,
|
||||
tipo: _,
|
||||
} = typed_f
|
||||
.arguments
|
||||
.first()
|
||||
|
@ -457,7 +465,7 @@ fn infer_definition(
|
|||
alias,
|
||||
parameters,
|
||||
annotation,
|
||||
..
|
||||
tipo: _,
|
||||
}) => {
|
||||
let tipo = environment
|
||||
.get_type_constructor(&None, &alias, location)
|
||||
|
@ -484,64 +492,66 @@ fn infer_definition(
|
|||
name,
|
||||
parameters,
|
||||
constructors: untyped_constructors,
|
||||
..
|
||||
typed_parameters: _,
|
||||
}) => {
|
||||
let constructors = untyped_constructors
|
||||
.into_iter()
|
||||
.map(
|
||||
|RecordConstructor {
|
||||
location,
|
||||
name,
|
||||
arguments: args,
|
||||
doc,
|
||||
sugar,
|
||||
}| {
|
||||
let preregistered_fn = environment
|
||||
.get_variable(&name)
|
||||
.expect("Could not find preregistered type for function");
|
||||
.map(|constructor| {
|
||||
let preregistered_fn = environment
|
||||
.get_variable(&constructor.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)) =
|
||||
preregistered_type.function_types()
|
||||
{
|
||||
args.into_iter()
|
||||
let args = preregistered_type.function_types().map_or(
|
||||
Ok(vec![]),
|
||||
|(args_types, _return_type)| {
|
||||
constructor
|
||||
.arguments
|
||||
.into_iter()
|
||||
.zip(&args_types)
|
||||
.map(
|
||||
|(
|
||||
RecordConstructorArg {
|
||||
label,
|
||||
annotation,
|
||||
location,
|
||||
doc,
|
||||
..
|
||||
},
|
||||
t,
|
||||
)| {
|
||||
RecordConstructorArg {
|
||||
label,
|
||||
annotation,
|
||||
location,
|
||||
tipo: t.clone(),
|
||||
doc,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
.map(|(arg, t)| {
|
||||
if t.is_function() {
|
||||
return Err(Error::FunctionTypeInData {
|
||||
location: arg.location,
|
||||
});
|
||||
}
|
||||
|
||||
RecordConstructor {
|
||||
location,
|
||||
name,
|
||||
arguments: args,
|
||||
doc,
|
||||
sugar,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
if t.is_ml_result() {
|
||||
return Err(Error::IllegalTypeInData {
|
||||
location: arg.location,
|
||||
tipo: t.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if t.contains_opaque() {
|
||||
let parent = environment
|
||||
.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
|
||||
.get_type_constructor(&None, &name, location)
|
||||
|
@ -561,7 +571,14 @@ fn infer_definition(
|
|||
};
|
||||
|
||||
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() {
|
||||
return Err(Error::FunctionTypeInData {
|
||||
location: *location,
|
||||
|
@ -585,7 +602,7 @@ fn infer_definition(
|
|||
module,
|
||||
as_name,
|
||||
unqualified,
|
||||
..
|
||||
package: _,
|
||||
}) => {
|
||||
let name = module.join("/");
|
||||
|
||||
|
@ -616,7 +633,7 @@ fn infer_definition(
|
|||
annotation,
|
||||
public,
|
||||
value,
|
||||
..
|
||||
tipo: _,
|
||||
}) => {
|
||||
let typed_expr =
|
||||
ExprTyper::new(environment, lines, tracing).infer_const(&annotation, *value)?;
|
||||
|
@ -672,7 +689,7 @@ fn infer_function(
|
|||
return_annotation,
|
||||
end_position,
|
||||
can_error,
|
||||
..
|
||||
return_type: _,
|
||||
} = f;
|
||||
|
||||
let preregistered_fn = environment
|
||||
|
@ -773,9 +790,18 @@ fn infer_fuzzer(
|
|||
};
|
||||
|
||||
match tipo.borrow() {
|
||||
Type::Fn { ret, .. } => match ret.borrow() {
|
||||
Type::Fn {
|
||||
ret,
|
||||
args: _,
|
||||
alias: _,
|
||||
} => match ret.borrow() {
|
||||
Type::App {
|
||||
module, name, args, ..
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
||||
match args.first().expect("args.len() == 1").borrow() {
|
||||
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
||||
|
@ -805,10 +831,13 @@ fn infer_fuzzer(
|
|||
_ => Err(could_not_unify()),
|
||||
},
|
||||
|
||||
Type::Var { tipo, .. } => match &*tipo.deref().borrow() {
|
||||
TypeVar::Link { tipo } => {
|
||||
infer_fuzzer(environment, expected_inner_type, tipo, location)
|
||||
}
|
||||
Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
|
||||
TypeVar::Link { tipo } => infer_fuzzer(
|
||||
environment,
|
||||
expected_inner_type,
|
||||
&Type::with_alias(tipo.clone(), alias.clone()),
|
||||
location,
|
||||
),
|
||||
_ => Err(Error::GenericLeftAtBoundary {
|
||||
location: *location,
|
||||
}),
|
||||
|
@ -821,7 +850,12 @@ fn infer_fuzzer(
|
|||
fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
|
||||
match tipo {
|
||||
Type::App {
|
||||
name, module, args, ..
|
||||
name,
|
||||
module,
|
||||
args,
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
} => {
|
||||
let arguments = args
|
||||
.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
|
||||
.iter()
|
||||
.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),
|
||||
_ => Err(Error::GenericLeftAtBoundary {
|
||||
location: *location,
|
||||
|
|
|
@ -141,11 +141,11 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
|||
pattern: UntypedPattern,
|
||||
tipo: Rc<Type>,
|
||||
ann_type: Option<Rc<Type>>,
|
||||
is_assignment: bool,
|
||||
is_let: bool,
|
||||
) -> Result<TypedPattern, Error> {
|
||||
match pattern {
|
||||
Pattern::Discard { name, location } => {
|
||||
if is_assignment {
|
||||
if is_let {
|
||||
// Register declaration for the unused variable detection
|
||||
self.environment
|
||||
.warnings
|
||||
|
@ -154,6 +154,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
|||
location,
|
||||
});
|
||||
};
|
||||
|
||||
Ok(Pattern::Discard { name, location })
|
||||
}
|
||||
|
||||
|
@ -202,7 +203,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
|||
location,
|
||||
elements,
|
||||
tail,
|
||||
} => match tipo.get_app_args(true, "", "List", 1, self.environment) {
|
||||
} => match tipo.get_app_args(true, false, "", "List", 1, self.environment) {
|
||||
Some(args) => {
|
||||
let tipo = args
|
||||
.first()
|
||||
|
|
|
@ -368,6 +368,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
},
|
||||
|
@ -378,12 +379,14 @@ mod tests {
|
|||
module: "".to_string(),
|
||||
name: "Pair".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
alias: None,
|
||||
args: vec![
|
||||
Rc::new(Type::App {
|
||||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
}),
|
||||
|
@ -391,6 +394,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
}),
|
||||
|
@ -406,6 +410,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
Rc::new(Type::App {
|
||||
|
@ -413,6 +418,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
],
|
||||
|
@ -421,6 +427,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
alias: None,
|
||||
|
@ -437,6 +444,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
|
@ -479,6 +487,7 @@ mod tests {
|
|||
Type::Fn {
|
||||
args: vec![Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -486,12 +495,14 @@ mod tests {
|
|||
})],
|
||||
ret: Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "Option".to_string(),
|
||||
args: vec![Rc::new(Type::Tuple {
|
||||
elems: vec![
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -499,6 +510,7 @@ mod tests {
|
|||
}),
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
args: vec![],
|
||||
|
@ -549,6 +561,7 @@ mod tests {
|
|||
Type::Fn {
|
||||
args: vec![Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -556,12 +569,14 @@ mod tests {
|
|||
})],
|
||||
ret: Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "Option".to_string(),
|
||||
args: vec![Rc::new(Type::Tuple {
|
||||
elems: vec![
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
|
|
@ -8,6 +8,7 @@ Schema {
|
|||
breadcrumbs: [
|
||||
App {
|
||||
public: true,
|
||||
contains_opaque: true,
|
||||
module: "test_module",
|
||||
name: "Rational",
|
||||
args: [],
|
||||
|
|
|
@ -8,6 +8,7 @@ Schema {
|
|||
breadcrumbs: [
|
||||
App {
|
||||
public: true,
|
||||
contains_opaque: true,
|
||||
module: "test_module",
|
||||
name: "Dict",
|
||||
args: [
|
||||
|
@ -16,6 +17,7 @@ Schema {
|
|||
value: Link {
|
||||
tipo: App {
|
||||
public: false,
|
||||
contains_opaque: false,
|
||||
module: "test_module",
|
||||
name: "UUID",
|
||||
args: [],
|
||||
|
@ -30,6 +32,7 @@ Schema {
|
|||
value: Link {
|
||||
tipo: App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
module: "",
|
||||
name: "Int",
|
||||
args: [],
|
||||
|
|
Loading…
Reference in New Issue