feat: block expects on opaque types
This commit is contained in:
parent
1d72838f83
commit
7af4ef53ab
|
@ -1225,6 +1225,7 @@ impl TypedPattern {
|
|||
}
|
||||
|
||||
pub fn tipo(&self, value: &TypedExpr) -> Option<Rc<Type>> {
|
||||
// expect thing: Wow = thing
|
||||
match self {
|
||||
Pattern::Int { .. } => Some(builtins::int()),
|
||||
Pattern::Constructor { tipo, .. } => Some(tipo.clone()),
|
||||
|
|
|
@ -1267,6 +1267,7 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
|
|||
pub fn int() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: INT.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
|
@ -1277,6 +1278,7 @@ pub fn int() -> Rc<Type> {
|
|||
pub fn data() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: DATA.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
|
@ -1288,6 +1290,7 @@ pub fn byte_array() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: BYTE_ARRAY.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1297,6 +1300,7 @@ pub fn byte_array() -> Rc<Type> {
|
|||
pub fn g1_element() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: G1_ELEMENT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1307,6 +1311,7 @@ pub fn g1_element() -> Rc<Type> {
|
|||
pub fn g2_element() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: G2_ELEMENT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1317,6 +1322,7 @@ pub fn g2_element() -> Rc<Type> {
|
|||
pub fn miller_loop_result() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: MILLER_LOOP_RESULT.to_string(),
|
||||
args: vec![],
|
||||
|
@ -1332,6 +1338,7 @@ pub fn bool() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: BOOL.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1342,6 +1349,7 @@ pub fn prng() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: PRNG.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1355,6 +1363,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
|||
name: "PRNG".to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
|
||||
Rc::new(Type::Fn {
|
||||
args: vec![prng()],
|
||||
ret: option(tuple(vec![prng(), a])),
|
||||
|
@ -1391,6 +1400,7 @@ pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
|||
pub fn list(t: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: LIST.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![t],
|
||||
|
@ -1402,6 +1412,7 @@ pub fn string() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: STRING.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1412,6 +1423,7 @@ pub fn void() -> Rc<Type> {
|
|||
Rc::new(Type::App {
|
||||
args: vec![],
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: VOID.to_string(),
|
||||
module: "".to_string(),
|
||||
alias: None,
|
||||
|
@ -1421,6 +1433,7 @@ pub fn void() -> Rc<Type> {
|
|||
pub fn option(a: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: OPTION.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![a],
|
||||
|
@ -1431,6 +1444,7 @@ pub fn option(a: Rc<Type>) -> Rc<Type> {
|
|||
pub fn ordering() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
name: ORDERING.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
|
@ -1448,17 +1462,20 @@ pub fn function(args: Vec<Rc<Type>>, ret: Rc<Type>) -> Rc<Type> {
|
|||
|
||||
pub fn generic_var(id: u64) -> Rc<Type> {
|
||||
let tipo = Rc::new(RefCell::new(TypeVar::Generic { id }));
|
||||
|
||||
Rc::new(Type::Var { tipo, alias: None })
|
||||
}
|
||||
|
||||
pub fn unbound_var(id: u64) -> Rc<Type> {
|
||||
let tipo = Rc::new(RefCell::new(TypeVar::Unbound { id }));
|
||||
|
||||
Rc::new(Type::Var { tipo, alias: None })
|
||||
}
|
||||
|
||||
pub fn wrapped_redeemer(redeemer: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: REDEEMER_WRAPPER.to_string(),
|
||||
args: vec![redeemer],
|
||||
|
|
|
@ -1793,3 +1793,41 @@ fn backpassing_type_annotation() {
|
|||
|
||||
assert!(check(parse(source_code)).is_ok())
|
||||
}
|
||||
|
||||
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() {
|
||||
let source_code = r#"
|
||||
opaque type Thing {
|
||||
Foo(Int)
|
||||
Bar(Int)
|
||||
}
|
||||
|
||||
fn bar(n: Thing) {
|
||||
expect Foo(a) = n
|
||||
|
||||
a
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(matches!(
|
||||
check(parse(source_code)),
|
||||
Err((_, Error::ExpectOnOpaqueType { .. }))
|
||||
))
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub enum Type {
|
|||
///
|
||||
App {
|
||||
public: bool,
|
||||
opaque: bool,
|
||||
module: String,
|
||||
name: String,
|
||||
args: Vec<Rc<Type>>,
|
||||
|
@ -152,12 +153,14 @@ impl Type {
|
|||
Rc::new(match self {
|
||||
Type::App {
|
||||
public,
|
||||
opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
..
|
||||
} => Type::App {
|
||||
public,
|
||||
opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
|
@ -181,17 +184,13 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_result_constructor(&self) -> bool {
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
match self {
|
||||
Type::Fn { ret, .. } => ret.is_result(),
|
||||
Type::App { opaque, .. } => *opaque,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_result(&self) -> bool {
|
||||
matches!(self, Self::App { name, module, .. } if "Result" == name && module.is_empty())
|
||||
}
|
||||
|
||||
pub fn is_unbound(&self) -> bool {
|
||||
matches!(self, Self::Var { tipo, .. } if tipo.borrow().is_unbound())
|
||||
}
|
||||
|
@ -459,6 +458,7 @@ impl Type {
|
|||
pub fn get_app_args(
|
||||
&self,
|
||||
public: bool,
|
||||
opaque: bool,
|
||||
module: &str,
|
||||
name: &str,
|
||||
arity: usize,
|
||||
|
@ -481,7 +481,7 @@ impl Type {
|
|||
Self::Var { tipo, alias } => {
|
||||
let args: Vec<_> = match tipo.borrow().deref() {
|
||||
TypeVar::Link { tipo } => {
|
||||
return tipo.get_app_args(public, module, name, arity, environment);
|
||||
return tipo.get_app_args(public, opaque, module, name, arity, environment);
|
||||
}
|
||||
|
||||
TypeVar::Unbound { .. } => {
|
||||
|
@ -496,6 +496,7 @@ impl Type {
|
|||
*tipo.borrow_mut() = TypeVar::Link {
|
||||
tipo: Rc::new(Self::App {
|
||||
public,
|
||||
opaque,
|
||||
name: name.to_string(),
|
||||
module: module.to_owned(),
|
||||
args: args.clone(),
|
||||
|
@ -650,6 +651,7 @@ pub fn convert_opaque_type(
|
|||
match t.as_ref() {
|
||||
Type::App {
|
||||
public,
|
||||
opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
|
@ -662,6 +664,7 @@ pub fn convert_opaque_type(
|
|||
}
|
||||
Type::App {
|
||||
public: *public,
|
||||
opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
args: new_args,
|
||||
|
@ -736,6 +739,7 @@ pub fn find_and_replace_generics(
|
|||
Type::App {
|
||||
args,
|
||||
public,
|
||||
opaque,
|
||||
module,
|
||||
name,
|
||||
alias,
|
||||
|
@ -748,6 +752,7 @@ pub fn find_and_replace_generics(
|
|||
let t = Type::App {
|
||||
args: new_args,
|
||||
public: *public,
|
||||
opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
alias: alias.clone(),
|
||||
|
|
|
@ -546,6 +546,7 @@ impl<'a> Environment<'a> {
|
|||
match t.deref() {
|
||||
Type::App {
|
||||
public,
|
||||
opaque,
|
||||
name,
|
||||
module,
|
||||
args,
|
||||
|
@ -555,8 +556,10 @@ impl<'a> Environment<'a> {
|
|||
.iter()
|
||||
.map(|t| self.instantiate(t.clone(), ids, hydrator))
|
||||
.collect();
|
||||
|
||||
Rc::new(Type::App {
|
||||
public: *public,
|
||||
opaque: *opaque,
|
||||
name: name.clone(),
|
||||
module: module.clone(),
|
||||
alias: alias.clone(),
|
||||
|
@ -983,6 +986,7 @@ impl<'a> Environment<'a> {
|
|||
Definition::DataType(DataType {
|
||||
name,
|
||||
public,
|
||||
opaque,
|
||||
parameters,
|
||||
location,
|
||||
constructors,
|
||||
|
@ -997,6 +1001,7 @@ impl<'a> Environment<'a> {
|
|||
|
||||
let tipo = Rc::new(Type::App {
|
||||
public: *public,
|
||||
opaque: *opaque,
|
||||
module: module.to_owned(),
|
||||
name: name.clone(),
|
||||
args: parameters.clone(),
|
||||
|
@ -1856,6 +1861,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
|
|||
|
||||
Type::App {
|
||||
public,
|
||||
opaque,
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
|
@ -1868,6 +1874,7 @@ pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
|
|||
|
||||
Rc::new(Type::App {
|
||||
public: *public,
|
||||
opaque: *opaque,
|
||||
module: module.clone(),
|
||||
name: name.clone(),
|
||||
args,
|
||||
|
|
|
@ -259,6 +259,13 @@ You can use '{discard}' and numbers to distinguish between similar names.
|
|||
name: String,
|
||||
},
|
||||
|
||||
#[error("I found an expect assignment involving an opaque type. This is not allowed.\n")]
|
||||
#[diagnostic(code("illegal::expect_on_opaque"))]
|
||||
ExpectOnOpaqueType {
|
||||
#[label]
|
||||
location: Span,
|
||||
},
|
||||
|
||||
#[error("I found a type definition that has a function type in it. This is not allowed.\n")]
|
||||
#[diagnostic(code("illegal::function_in_type"))]
|
||||
#[diagnostic(help(
|
||||
|
@ -1045,6 +1052,7 @@ impl ExtraData for Error {
|
|||
| Error::IncorrectTestArity { .. }
|
||||
| Error::GenericLeftAtBoundary { .. }
|
||||
| Error::UnexpectedMultiPatternAssignment { .. }
|
||||
| Error::ExpectOnOpaqueType { .. }
|
||||
| Error::ValidatorMustReturnBool { .. } => None,
|
||||
|
||||
Error::UnknownType { name, .. }
|
||||
|
|
|
@ -932,6 +932,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
let mut value_typ = typed_value.tipo();
|
||||
|
||||
let value_is_data = value_typ.is_data();
|
||||
let value_is_opaque = value_typ.is_opaque();
|
||||
|
||||
// Check that any type annotation is accurate.
|
||||
let pattern = if let Some(ann) = annotation {
|
||||
|
@ -939,6 +940,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
.type_from_annotation(ann)
|
||||
.map(|t| self.instantiate(t, &mut HashMap::new()))?;
|
||||
|
||||
if kind.is_expect() && (ann_typ.is_opaque() || value_is_opaque) {
|
||||
return Err(Error::ExpectOnOpaqueType { location });
|
||||
}
|
||||
|
||||
self.unify(
|
||||
ann_typ.clone(),
|
||||
value_typ.clone(),
|
||||
|
@ -975,6 +980,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
});
|
||||
}
|
||||
|
||||
if kind.is_expect() && value_is_opaque {
|
||||
return Err(Error::ExpectOnOpaqueType { location });
|
||||
}
|
||||
|
||||
// Ensure the pattern matches the type of the value
|
||||
PatternTyper::new(self.environment, &self.hydrator).unify(
|
||||
untyped_pattern.clone(),
|
||||
|
|
|
@ -202,7 +202,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
|||
location,
|
||||
elements,
|
||||
tail,
|
||||
} => match tipo.get_app_args(true, "", "List", 1, self.environment) {
|
||||
} => match tipo.get_app_args(true, false, "", "List", 1, self.environment) {
|
||||
Some(args) => {
|
||||
let tipo = args
|
||||
.first()
|
||||
|
|
|
@ -368,6 +368,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
},
|
||||
|
@ -378,12 +379,14 @@ mod tests {
|
|||
module: "".to_string(),
|
||||
name: "Pair".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
alias: None,
|
||||
args: vec![
|
||||
Rc::new(Type::App {
|
||||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
}),
|
||||
|
@ -391,6 +394,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
args: vec![],
|
||||
alias: None
|
||||
}),
|
||||
|
@ -406,6 +410,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
Rc::new(Type::App {
|
||||
|
@ -413,6 +418,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
],
|
||||
|
@ -421,6 +427,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
alias: None,
|
||||
}),
|
||||
alias: None,
|
||||
|
@ -437,6 +444,7 @@ mod tests {
|
|||
module: "whatever".to_string(),
|
||||
name: "Int".to_string(),
|
||||
public: true,
|
||||
opaque: false,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
|
@ -479,6 +487,7 @@ mod tests {
|
|||
Type::Fn {
|
||||
args: vec![Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -486,12 +495,14 @@ mod tests {
|
|||
})],
|
||||
ret: Rc::new(Type::App {
|
||||
public: true,
|
||||
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,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -499,6 +510,7 @@ mod tests {
|
|||
}),
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "Bool".to_string(),
|
||||
args: vec![],
|
||||
|
@ -549,6 +561,7 @@ mod tests {
|
|||
Type::Fn {
|
||||
args: vec![Rc::new(Type::App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
@ -556,12 +569,14 @@ mod tests {
|
|||
})],
|
||||
ret: Rc::new(Type::App {
|
||||
public: true,
|
||||
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,
|
||||
module: "".to_string(),
|
||||
name: "PRNG".to_string(),
|
||||
args: vec![],
|
||||
|
|
|
@ -8,6 +8,7 @@ Schema {
|
|||
breadcrumbs: [
|
||||
App {
|
||||
public: true,
|
||||
opaque: true,
|
||||
module: "test_module",
|
||||
name: "Rational",
|
||||
args: [],
|
||||
|
|
|
@ -8,6 +8,7 @@ Schema {
|
|||
breadcrumbs: [
|
||||
App {
|
||||
public: true,
|
||||
opaque: true,
|
||||
module: "test_module",
|
||||
name: "Dict",
|
||||
args: [
|
||||
|
@ -16,6 +17,7 @@ Schema {
|
|||
value: Link {
|
||||
tipo: App {
|
||||
public: false,
|
||||
opaque: false,
|
||||
module: "test_module",
|
||||
name: "UUID",
|
||||
args: [],
|
||||
|
@ -30,6 +32,7 @@ Schema {
|
|||
value: Link {
|
||||
tipo: App {
|
||||
public: true,
|
||||
opaque: false,
|
||||
module: "",
|
||||
name: "Int",
|
||||
args: [],
|
||||
|
|
Loading…
Reference in New Issue