Skip to content

Commit

Permalink
fix: incoherences in the type system regarding cyclic validation
Browse files Browse the repository at this point in the history
  • Loading branch information
delehef committed Aug 28, 2023
1 parent 04fa4ef commit 12c95e2
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/compiler/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl FuncVerifier<Node> for Builtin {
Builtin::Len => &[&[Type::ArrayColumn(Magma::Any)]],
};

if super::cyclic_compatible_with(expected_t, &args_t) {
if super::compatible_with_repeating(expected_t, &args_t) {
Ok(())
} else {
bail!(CompileError::TypeError(
Expand Down
33 changes: 12 additions & 21 deletions src/compiler/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,35 +217,26 @@ impl FuncVerifier<Node> for Intrinsic {
// Each nested second-level list represent the acceptable typings for
// the argument they type.
//
// The first-level list is cycled as many times is needed to validate
// all the arguments. Therefore, for function taking homogeneous
// arguments (e.g. Add), a single second-level list is enough.
// The first-level list last element is cycled as many times is needed
// to validate all the arguments. Therefore, for function taking
// homogeneous arguments (e.g. Add), a single second-level list is
// enough.
let expected_t: &[&[Type]] = match self {
Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul => &[&[Type::Any(Magma::Any)]],
Intrinsic::Exp => &[&[Type::Any(Magma::Any)], &[Type::Scalar(Magma::Any)]],
Intrinsic::Neg => &[&[Type::Scalar(Magma::Any), Type::Column(Magma::Any)]],
Intrinsic::Inv => &[&[Type::Column(Magma::Any)]],
Intrinsic::Inv => &[&[Type::Any(Magma::Any)]],
Intrinsic::Shift => &[&[Type::Column(Magma::Any)], &[Type::Scalar(Magma::Any)]],
Intrinsic::IfZero | Intrinsic::IfNotZero => &[
&[
Type::Scalar(Magma::Any),
Type::Column(Magma::Any),
Type::List(Magma::Any),
],
&[
Type::Scalar(Magma::Any),
Type::Column(Magma::Any),
Type::List(Magma::Any),
],
// condition type TODO: force to bool/loob
&[Type::Any(Magma::Any)],
// then/else arms type
&[Type::Any(Magma::Any), Type::List(Magma::Any)],
],
Intrinsic::Begin => &[&[
Type::Scalar(Magma::Any),
Type::Column(Magma::Any),
Type::List(Magma::Any),
]],
Intrinsic::Begin => &[&[Type::Any(Magma::Any)]],
};

if super::cyclic_compatible_with(expected_t, &args_t) {
if super::compatible_with_repeating(expected_t, &args_t) {
Ok(())
} else {
bail!(CompileError::TypeError(
Expand Down Expand Up @@ -1152,7 +1143,7 @@ fn apply_intrinsic(
} else {
traversed_args[0].to_string()
};
error!("condition {} may overflow", pretty.bright_white().bold());
warn!("condition {} may overflow", pretty.bright_white().bold());
}
Ok(Some(r))
}
Expand Down
15 changes: 9 additions & 6 deletions src/compiler/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,15 @@ impl Node {
Expression::Funcall { func, args } => {
let mut mine = ax;
if let Intrinsic::Shift = func {
let arg_big = args[1]
.pure_eval()
.unwrap_or_else(|_| panic!("{}", args[1].to_string().as_str()));
let arg = arg_big
.to_isize()
.unwrap_or_else(|| panic!("{}", arg_big.to_string().as_str()));
let arg_big = args[1].pure_eval().unwrap_or_else(|_| {
panic!(
"{} is not a valid shift offset",
args[1].to_string().as_str()
)
});
let arg = arg_big.to_isize().unwrap_or_else(|| {
panic!("{} is not an isize", arg_big.to_string().as_str())
});
mine = mine.min(mine + arg);
}
args.iter().map(|e| _past_span(e.e(), mine)).min().unwrap()
Expand Down
30 changes: 24 additions & 6 deletions src/compiler/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl std::fmt::Display for Type {
Type::Void => write!(f, "∅"),
Type::Scalar(m) => write!(f, "{}", m),
Type::Column(m) => write!(f, "[{}]", m),
Type::Any(m) => write!(f, "{}", m),
Type::Any(m) => write!(f, "{}", m),
Type::ArrayColumn(m) => write!(f, "[[{}]]", m),
Type::List(m) => write!(f, "{{{}}}", m),
}
Expand Down Expand Up @@ -290,23 +290,41 @@ impl std::fmt::Display for Magma {
Magma::None => write!(f, "NONE"),
Magma::Loobean => write!(f, "𝕃"),
Magma::Boolean => write!(f, "𝔹"),
Magma::Nibble => write!(f, "Nib."),
Magma::Byte => write!(f, "Byte"),
Magma::Nibble => write!(f, ""),
Magma::Byte => write!(f, ""),
Magma::Integer => write!(f, "Fr"),
Magma::Any => write!(f, "∀"),
}
}
}

pub fn cyclic_compatible_with(expected: &[&[Type]], found: &[Type]) -> bool {
for (es, f) in expected.iter().cycle().zip(found.iter()) {
if !es.iter().any(|e| e >= f) {
/// Checks that a given list of types `found` is compatible with a list of list
/// of types `expected`. For each position in the lists, the type in `found`
/// must match with one of the types in the corresponding nested list in
/// `expected` for the operation to be a success.
///
/// In the case where `found` is longer than `expected`, the last element of
/// `expected` is repeated as many times as required. This allows for typing
/// variadic functions, as long as their tail of arguments is type-homogeneous.
pub fn compatible_with_repeating(expected: &[&[Type]], found: &[Type]) -> bool {
for (es, f) in expected
.iter()
.chain(std::iter::repeat(expected.last().unwrap()))
.zip(found.iter())
{
if !es.iter().any(|e| f.can_cast_to(*e)) {
return false;
}
}
true
}

/// Checks that a given list of types `found` is compatible with a list of list
/// of types `expected`. For each position in the lists, the type in `found`
/// must match with the corresponding type `expected` for the operation to be a
/// success.
///
/// If `found` and `expected` differ in length, the operation is a failure.
pub fn compatible_with(expected: &[Type], found: &[Type]) -> bool {
if expected.len() != found.len() {
return false;
Expand Down
12 changes: 7 additions & 5 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,16 @@ pub(crate) mod compiler {
expected
.iter()
.cycle()
.chain(std::iter::repeat(expected.last().unwrap()))
.zip(found.iter())
.map(|(es, f)| {
let es_str = es
.iter()
.map(|e| format!("{:?}", e))
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("|");
if es.iter().any(|e| e >= f) {
es_str.black().to_string()
if es.iter().any(|e| f.can_cast_to(*e)) {
es_str.white().to_string()
} else {
es_str.blue().to_string()
}
Expand All @@ -139,10 +140,11 @@ pub(crate) mod compiler {
expected
.iter()
.cycle()
.chain(std::iter::repeat(expected.last().unwrap()))
.zip(found.iter())
.map(|(e, f)| {
if e.iter().any(|e| e >= f) {
f.black().to_string()
if e.iter().any(|e| f.can_cast_to(*e)) {
f.white().to_string()
} else {
f.red().to_string()
}
Expand Down

0 comments on commit 12c95e2

Please sign in to comment.