Do not allow casting when rhs or lhs contain an opaque type.
Also slightly extended the check test 'framework' to allow registering side-dependency and using them from another module. This allows to check the interplay between opaque type from within and outside of their host module.
This commit is contained in:
parent
191a3e9134
commit
3055c5ef52
|
@ -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]
|
||||
|
@ -1813,7 +1836,7 @@ fn forbid_expect_into_opaque_type_from_data() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn forbid_expect_into_opaque_type_constructor_without_typecasting() {
|
||||
fn forbid_expect_into_opaque_type_constructor_without_typecasting_in_module() {
|
||||
let source_code = r#"
|
||||
opaque type Thing {
|
||||
Foo(Int)
|
||||
|
@ -1826,10 +1849,51 @@ fn forbid_expect_into_opaque_type_constructor_without_typecasting() {
|
|||
}
|
||||
"#;
|
||||
|
||||
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(parse(source_code)),
|
||||
Err((_, Error::ExpectOnOpaqueType { .. }))
|
||||
))
|
||||
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]
|
||||
|
@ -1914,7 +1978,6 @@ fn allow_expect_on_var_patterns_that_are_opaque() {
|
|||
|
||||
fn bar(a: Option<Thing>) {
|
||||
expect Some(thing) = a
|
||||
|
||||
thing.inner
|
||||
}
|
||||
"#;
|
||||
|
|
|
@ -1415,10 +1415,15 @@ 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, alias } = t2.deref() {
|
||||
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
|
||||
|
|
|
@ -976,10 +976,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
kind.is_let(),
|
||||
)?;
|
||||
|
||||
if kind.is_expect() && value_typ.contains_opaque() {
|
||||
return Err(Error::ExpectOnOpaqueType { location });
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
Loading…
Reference in New Issue