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(
|
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]
|
||||||
|
@ -1813,7 +1836,7 @@ fn forbid_expect_into_opaque_type_from_data() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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#"
|
let source_code = r#"
|
||||||
opaque type Thing {
|
opaque type Thing {
|
||||||
Foo(Int)
|
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!(
|
assert!(matches!(
|
||||||
check(parse(source_code)),
|
check_with_deps(
|
||||||
Err((_, Error::ExpectOnOpaqueType { .. }))
|
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]
|
#[test]
|
||||||
|
@ -1914,7 +1978,6 @@ fn allow_expect_on_var_patterns_that_are_opaque() {
|
||||||
|
|
||||||
fn bar(a: Option<Thing>) {
|
fn bar(a: Option<Thing>) {
|
||||||
expect Some(thing) = a
|
expect Some(thing) = a
|
||||||
|
|
||||||
thing.inner
|
thing.inner
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
|
@ -1415,10 +1415,15 @@ 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, alias } = 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() {
|
||||||
|
|
|
@ -976,10 +976,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
kind.is_let(),
|
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
|
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
|
||||||
// error we emit a warning which explains that using `expect` is unnecessary.
|
// error we emit a warning which explains that using `expect` is unnecessary.
|
||||||
match kind {
|
match kind {
|
||||||
|
|
Loading…
Reference in New Issue