Introduce 'Never' type as a safe alternative to always None options
Unfortunately, as documented in: https://github.com/IntersectMBO/cardano-ledger/issues/4571 Some Option fields in the script context certificates are going to remain set to None, at least until the next Hard fork. There's a risk that people permanently lock their funds if they expect deposits on registration credentials to ever be `Some`. So, we introduce a special type that emulate an `Option` that can only ever be `None`. We call it `Never` and it is the first type of this kind (i.e. with constructors indexes not starting at 0).
This commit is contained in:
@@ -400,6 +400,19 @@ impl TypedDataType {
|
||||
doc: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_never(&self) -> bool {
|
||||
self.name == well_known::NEVER
|
||||
&& self.constructors.len() == well_known::NEVER_CONSTRUCTORS.len()
|
||||
&& self.location == Span::empty()
|
||||
&& self
|
||||
.constructors
|
||||
.iter()
|
||||
.zip(well_known::NEVER_CONSTRUCTORS)
|
||||
.all(|(constructor, name)| {
|
||||
name == &constructor.name && constructor.arguments.is_empty()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
@@ -16,6 +16,8 @@ pub const LIST: &str = "List";
|
||||
pub const MILLER_LOOP_RESULT: &str = "MillerLoopResult";
|
||||
pub const OPTION: &str = "Option";
|
||||
pub const OPTION_CONSTRUCTORS: &[&str] = &["Some", "None"];
|
||||
pub const NEVER: &str = "Never";
|
||||
pub const NEVER_CONSTRUCTORS: &[&str] = &["__hole", "Never"];
|
||||
pub const ORDERING: &str = "Ordering";
|
||||
pub const ORDERING_CONSTRUCTORS: &[&str] = &["Less", "Equal", "Greater"];
|
||||
pub const PAIR: &str = "Pair";
|
||||
@@ -297,6 +299,17 @@ impl Type {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn never() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
contains_opaque: false,
|
||||
name: NEVER.to_string(),
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
alias: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ordering() -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
|
||||
@@ -168,6 +168,19 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
||||
),
|
||||
);
|
||||
|
||||
// Never
|
||||
prelude.types.insert(
|
||||
well_known::NEVER.to_string(),
|
||||
TypeConstructor::primitive(Type::never()),
|
||||
);
|
||||
prelude.types_constructors.insert(
|
||||
well_known::NEVER.to_string(),
|
||||
ValueConstructor::known_adt(
|
||||
&mut prelude.values,
|
||||
&[(well_known::NEVER_CONSTRUCTORS[1], Type::never())],
|
||||
),
|
||||
);
|
||||
|
||||
// Cardano ScriptContext
|
||||
prelude.types.insert(
|
||||
well_known::SCRIPT_CONTEXT.to_string(),
|
||||
@@ -1503,6 +1516,15 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
|
||||
option_data_type,
|
||||
);
|
||||
|
||||
// Never
|
||||
data_types.insert(
|
||||
DataTypeKey {
|
||||
module_name: "".to_string(),
|
||||
defined_type: well_known::NEVER.to_string(),
|
||||
},
|
||||
TypedDataType::never(),
|
||||
);
|
||||
|
||||
// PRNG
|
||||
let prng_data_type = TypedDataType::prng();
|
||||
data_types.insert(
|
||||
@@ -1609,4 +1631,8 @@ impl TypedDataType {
|
||||
typed_parameters: vec![tipo],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn never() -> Self {
|
||||
DataType::known_enum(well_known::NEVER, well_known::NEVER_CONSTRUCTORS)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1434,7 +1434,9 @@ impl<'a> CodeGenerator<'a> {
|
||||
.unwrap_or_else(|| unreachable!("Failed to find definition for {}", name));
|
||||
|
||||
let then = if props.kind.is_expect()
|
||||
&& (data_type.constructors.len() > 1 || props.full_check)
|
||||
&& (data_type.constructors.len() > 1
|
||||
|| props.full_check
|
||||
|| data_type.is_never())
|
||||
{
|
||||
let (index, _) = data_type
|
||||
.constructors
|
||||
@@ -1459,8 +1461,8 @@ impl<'a> CodeGenerator<'a> {
|
||||
)
|
||||
} else {
|
||||
assert!(
|
||||
data_type.constructors.len() == 1,
|
||||
"attempted expect on a type with more or less than 1 constructor: \nis_expect? {}\nfull_check? {}\ndata_type={data_type:#?}\n{}",
|
||||
data_type.constructors.len() == 1 || data_type.is_never(),
|
||||
"attempted let-assignment on a type with more or less than 1 constructor: \nis_expect? {}\nfull_check? {}\ndata_type={data_type:#?}\n{}",
|
||||
props.kind.is_expect(),
|
||||
props.full_check,
|
||||
name,
|
||||
@@ -1978,9 +1980,20 @@ impl<'a> CodeGenerator<'a> {
|
||||
|
||||
let otherwise_delayed = AirTree::local_var("otherwise_delayed", Type::void());
|
||||
|
||||
let is_never = data_type.is_never();
|
||||
|
||||
let constr_clauses = data_type.constructors.iter().enumerate().rfold(
|
||||
otherwise_delayed.clone(),
|
||||
|acc, (index, constr)| {
|
||||
// NOTE: For the Never type, we have an placeholder first constructor
|
||||
// that must be ignored. The Never type is considered to have only one
|
||||
// constructor starting at index 1 so it shouldn't be possible to
|
||||
// cast from Data into the first constructor. There's virtually no
|
||||
// constructor at index 0.
|
||||
if is_never && index == 0 {
|
||||
return acc;
|
||||
}
|
||||
|
||||
let mut constr_args = vec![];
|
||||
|
||||
let constr_then = constr.arguments.iter().enumerate().rfold(
|
||||
@@ -2169,7 +2182,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
),
|
||||
)
|
||||
} else if let Some(data_type) = data_type {
|
||||
if data_type.constructors.len() > 1 {
|
||||
if data_type.constructors.len() > 1 && !data_type.is_never() {
|
||||
AirTree::clause(
|
||||
&props.original_subject_name,
|
||||
clause_cond,
|
||||
@@ -5584,8 +5597,18 @@ impl<'a> CodeGenerator<'a> {
|
||||
} => {
|
||||
let tail_name_prefix = "__tail_index";
|
||||
|
||||
let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo)
|
||||
.unwrap_or_else(|| panic!("HOW DID YOU DO THIS ON BOOL OR VOID"));
|
||||
let data_type =
|
||||
lookup_data_type_by_tipo(&self.data_types, &tipo).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Attempted record update on an unknown type!\ntype: {:#?}",
|
||||
tipo
|
||||
)
|
||||
});
|
||||
|
||||
assert!(
|
||||
!data_type.is_never(),
|
||||
"Attempted record update on a Never type.",
|
||||
);
|
||||
|
||||
let constructor_field_count = data_type.constructors[0].arguments.len();
|
||||
let record = arg_stack.pop().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user