feat: fix some tests and add a failing one

This commit is contained in:
rvcas 2024-03-13 20:15:27 -04:00
parent 9127dcdd6e
commit e71470747f
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
8 changed files with 164 additions and 119 deletions

View File

@ -1267,7 +1267,7 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
pub fn int() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
name: INT.to_string(),
module: "".to_string(),
args: vec![],
@ -1278,7 +1278,7 @@ pub fn int() -> Rc<Type> {
pub fn data() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
name: DATA.to_string(),
module: "".to_string(),
args: vec![],
@ -1290,7 +1290,7 @@ pub fn byte_array() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
opaque: false,
contains_opaque: false,
name: BYTE_ARRAY.to_string(),
module: "".to_string(),
alias: None,
@ -1300,7 +1300,7 @@ pub fn byte_array() -> Rc<Type> {
pub fn g1_element() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: G1_ELEMENT.to_string(),
args: vec![],
@ -1311,7 +1311,7 @@ pub fn g1_element() -> Rc<Type> {
pub fn g2_element() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: G2_ELEMENT.to_string(),
args: vec![],
@ -1322,7 +1322,7 @@ pub fn g2_element() -> Rc<Type> {
pub fn miller_loop_result() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: MILLER_LOOP_RESULT.to_string(),
args: vec![],
@ -1338,7 +1338,7 @@ pub fn bool() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
opaque: false,
contains_opaque: false,
name: BOOL.to_string(),
module: "".to_string(),
alias: None,
@ -1349,7 +1349,7 @@ pub fn prng() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
opaque: false,
contains_opaque: false,
name: PRNG.to_string(),
module: "".to_string(),
alias: None,
@ -1400,7 +1400,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
pub fn list(t: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
name: LIST.to_string(),
module: "".to_string(),
args: vec![t],
@ -1412,7 +1412,7 @@ pub fn string() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
opaque: false,
contains_opaque: false,
name: STRING.to_string(),
module: "".to_string(),
alias: None,
@ -1423,7 +1423,7 @@ pub fn void() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
opaque: false,
contains_opaque: false,
name: VOID.to_string(),
module: "".to_string(),
alias: None,
@ -1433,7 +1433,7 @@ pub fn void() -> Rc<Type> {
pub fn option(a: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
name: OPTION.to_string(),
module: "".to_string(),
args: vec![a],
@ -1444,7 +1444,7 @@ pub fn option(a: Rc<Type>) -> Rc<Type> {
pub fn ordering() -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
name: ORDERING.to_string(),
module: "".to_string(),
args: vec![],
@ -1475,7 +1475,7 @@ pub fn unbound_var(id: u64) -> Rc<Type> {
pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> {
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: REDEEMER_WRAPPER.to_string(),
args: vec![redeemer],

View File

@ -1858,7 +1858,7 @@ fn forbid_expect_into_nested_opaque_in_record_without_typecasting() {
type Foo { foo: Thing }
fn bar(thing: Thing) {
fn bar(thing: Foo) {
expect Foo { foo: Thing { inner } } : Foo = thing
Void
}
@ -1905,3 +1905,18 @@ fn forbid_expect_into_nested_opaque_in_list() {
Err((_, Error::ExpectOnOpaqueType { .. }))
))
}
#[test]
fn allow_expect_on_var_patterns_that_are_opaque() {
let source_code = r#"
opaque type Thing { inner: Int }
fn bar(a: Option<Thing>) {
expect Some(thing) = a
thing.inner
}
"#;
assert!(check(parse(source_code)).is_ok())
}

View File

@ -42,7 +42,7 @@ pub enum Type {
///
App {
public: bool,
opaque: bool,
contains_opaque: bool,
module: String,
name: String,
args: Vec<Rc<Type>>,
@ -81,7 +81,7 @@ impl PartialEq for Type {
module,
name,
args,
opaque,
contains_opaque: opaque,
alias: _,
} => {
if let Type::App {
@ -89,7 +89,7 @@ impl PartialEq for Type {
module: module2,
name: name2,
args: args2,
opaque: opaque2,
contains_opaque: opaque2,
alias: _,
} = other
{
@ -160,14 +160,14 @@ impl Type {
Rc::new(match self {
Type::App {
public,
opaque,
contains_opaque: opaque,
module,
name,
args,
alias: _,
} => Type::App {
public,
opaque,
contains_opaque: opaque,
module,
name,
args,
@ -195,17 +195,30 @@ impl Type {
}
}
pub fn is_or_holds_opaque(&self) -> bool {
pub fn contains_opaque(&self) -> bool {
match self {
Type::Var { tipo, .. } => tipo.borrow().is_or_holds_opaque(),
Type::App { opaque, args, .. } => {
*opaque || args.iter().any(|arg| arg.is_or_holds_opaque())
}
Type::Tuple { elems, .. } => elems.iter().any(|elem| elem.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 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 {
matches!(self, Self::Var { tipo, .. } if tipo.borrow().is_unbound())
}
@ -511,7 +524,7 @@ impl Type {
*tipo.borrow_mut() = TypeVar::Link {
tipo: Rc::new(Self::App {
public,
opaque,
contains_opaque: opaque,
name: name.to_string(),
module: module.to_owned(),
args: args.clone(),
@ -666,7 +679,7 @@ pub fn convert_opaque_type(
match t.as_ref() {
Type::App {
public,
opaque,
contains_opaque: opaque,
module,
name,
args,
@ -679,7 +692,7 @@ pub fn convert_opaque_type(
}
Type::App {
public: *public,
opaque: *opaque,
contains_opaque: *opaque,
module: module.clone(),
name: name.clone(),
args: new_args,
@ -754,7 +767,7 @@ pub fn find_and_replace_generics(
Type::App {
args,
public,
opaque,
contains_opaque: opaque,
module,
name,
alias,
@ -767,7 +780,7 @@ pub fn find_and_replace_generics(
let t = Type::App {
args: new_args,
public: *public,
opaque: *opaque,
contains_opaque: *opaque,
module: module.clone(),
name: name.clone(),
alias: alias.clone(),
@ -851,7 +864,7 @@ impl TypeVar {
pub fn is_or_holds_opaque(&self) -> bool {
match self {
Self::Link { tipo } => tipo.is_or_holds_opaque(),
Self::Link { tipo } => tipo.contains_opaque(),
_ => false,
}
}
@ -981,13 +994,11 @@ impl TypeVar {
Self::Link { tipo } => tipo.get_inner_types(),
Self::Unbound { .. } => vec![],
var => {
vec![
Type::Var {
tipo: RefCell::new(var.clone()).into(),
alias: None,
}
.into(),
]
vec![Type::Var {
tipo: RefCell::new(var.clone()).into(),
alias: None,
}
.into()]
}
}
}

View File

@ -301,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,
@ -551,7 +570,7 @@ impl<'a> Environment<'a> {
match t.deref() {
Type::App {
public,
opaque,
contains_opaque: opaque,
name,
module,
args,
@ -564,7 +583,7 @@ impl<'a> Environment<'a> {
Rc::new(Type::App {
public: *public,
opaque: *opaque,
contains_opaque: *opaque,
name: name.clone(),
module: module.clone(),
alias: alias.clone(),
@ -1006,7 +1025,7 @@ impl<'a> Environment<'a> {
let tipo = Rc::new(Type::App {
public: *public,
opaque: *opaque,
contains_opaque: *opaque,
module: module.to_owned(),
name: name.clone(),
args: parameters.clone(),
@ -1471,7 +1490,7 @@ impl<'a> Environment<'a> {
name: n1,
args: args1,
public: _,
opaque: _,
contains_opaque: _,
alias: _,
},
Type::App {
@ -1479,7 +1498,7 @@ impl<'a> Environment<'a> {
name: n2,
args: args2,
public: _,
opaque: _,
contains_opaque: _,
alias: _,
},
) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => {
@ -1742,7 +1761,7 @@ fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(),
name: _,
public: _,
alias: _,
opaque: _,
contains_opaque: _,
} => {
for arg in args {
unify_unbound_type(arg.clone(), own_id, location)?
@ -1906,7 +1925,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
Type::App {
public,
opaque,
contains_opaque: opaque,
module,
name,
args,
@ -1919,7 +1938,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
Rc::new(Type::App {
public: *public,
opaque: *opaque,
contains_opaque: *opaque,
module: module.clone(),
name: name.clone(),
args,

View File

@ -976,11 +976,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
kind.is_let(),
)?;
// FIXME: This check is insufficient as we need to also assert the type
// definition itself since there might be nested opaque types on the rhs.
//
// For that, we must lookup
if kind.is_expect() && value_typ.is_or_holds_opaque() {
if kind.is_expect() && value_typ.contains_opaque() {
return Err(Error::ExpectOnOpaqueType { location });
}
@ -2347,7 +2343,7 @@ pub fn ensure_serialisable(allow_fn: bool, t: Rc<Type>, location: Span) -> Resul
name: _,
module: _,
public: _,
opaque: _,
contains_opaque: _,
alias: _,
} => {
if t.is_ml_result() {

View File

@ -89,6 +89,7 @@ impl UntypedModule {
&self.lines,
tracing,
)?;
definitions.push(definition);
}
@ -495,60 +496,62 @@ fn infer_definition(
}) => {
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,
tipo: _,
},
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)
@ -797,7 +800,7 @@ fn infer_fuzzer(
name,
args,
public: _,
opaque: _,
contains_opaque: _,
alias: _,
} if module.is_empty() && name == "Option" && args.len() == 1 => {
match args.first().expect("args.len() == 1").borrow() {
@ -851,7 +854,7 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
module,
args,
public: _,
opaque: _,
contains_opaque: _,
alias: _,
} => {
let arguments = args

View File

@ -154,6 +154,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
location,
});
};
Ok(Pattern::Discard { name, location })
}

View File

@ -368,7 +368,7 @@ mod tests {
module: "whatever".to_string(),
name: "Int".to_string(),
public: true,
opaque: false,
contains_opaque: false,
args: vec![],
alias: None
},
@ -379,14 +379,14 @@ mod tests {
module: "".to_string(),
name: "Pair".to_string(),
public: true,
opaque: false,
contains_opaque: false,
alias: None,
args: vec![
Rc::new(Type::App {
module: "whatever".to_string(),
name: "Int".to_string(),
public: true,
opaque: false,
contains_opaque: false,
args: vec![],
alias: None
}),
@ -394,7 +394,7 @@ mod tests {
module: "whatever".to_string(),
name: "Bool".to_string(),
public: true,
opaque: false,
contains_opaque: false,
args: vec![],
alias: None
}),
@ -410,7 +410,7 @@ mod tests {
module: "whatever".to_string(),
name: "Int".to_string(),
public: true,
opaque: false,
contains_opaque: false,
alias: None,
}),
Rc::new(Type::App {
@ -418,7 +418,7 @@ mod tests {
module: "whatever".to_string(),
name: "Bool".to_string(),
public: true,
opaque: false,
contains_opaque: false,
alias: None,
}),
],
@ -427,7 +427,7 @@ mod tests {
module: "whatever".to_string(),
name: "Bool".to_string(),
public: true,
opaque: false,
contains_opaque: false,
alias: None,
}),
alias: None,
@ -444,7 +444,7 @@ mod tests {
module: "whatever".to_string(),
name: "Int".to_string(),
public: true,
opaque: false,
contains_opaque: false,
}),
})),
},
@ -487,7 +487,7 @@ mod tests {
Type::Fn {
args: vec![Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "PRNG".to_string(),
args: vec![],
@ -495,14 +495,14 @@ mod tests {
})],
ret: Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "Option".to_string(),
args: vec![Rc::new(Type::Tuple {
elems: vec![
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "PRNG".to_string(),
args: vec![],
@ -510,7 +510,7 @@ mod tests {
}),
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "Bool".to_string(),
args: vec![],
@ -561,7 +561,7 @@ mod tests {
Type::Fn {
args: vec![Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "PRNG".to_string(),
args: vec![],
@ -569,14 +569,14 @@ mod tests {
})],
ret: Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "Option".to_string(),
args: vec![Rc::new(Type::Tuple {
elems: vec![
Rc::new(Type::App {
public: true,
opaque: false,
contains_opaque: false,
module: "".to_string(),
name: "PRNG".to_string(),
args: vec![],