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> {
|
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],
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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, .. }
|
||||||
|
|
|
@ -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,14 +965,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Ensure the pattern matches the type of the value
|
// Ensure the pattern matches the type of the value
|
||||||
PatternTyper::new(self.environment, &self.hydrator).unify(
|
let pattern = PatternTyper::new(self.environment, &self.hydrator).unify(
|
||||||
untyped_pattern.clone(),
|
untyped_pattern.clone(),
|
||||||
value_typ.clone(),
|
value_typ.clone(),
|
||||||
None,
|
ann_typ,
|
||||||
kind.is_let(),
|
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.
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
|
||||||
location,
|
|
||||||
name,
|
|
||||||
arguments: args,
|
|
||||||
doc,
|
|
||||||
sugar,
|
|
||||||
}| {
|
|
||||||
let preregistered_fn = environment
|
let preregistered_fn = environment
|
||||||
.get_variable(&name)
|
.get_variable(&constructor.name)
|
||||||
.expect("Could not find preregistered type for function");
|
.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
|
||||||
|
.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()
|
||||||
},
|
},
|
||||||
)
|
)?;
|
||||||
.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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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![],
|
||||||
|
|
|
@ -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: [],
|
||||||
|
|
|
@ -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: [],
|
||||||
|
|
Loading…
Reference in New Issue