mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
First pass constraining crash
This commit is contained in:
parent
ee5eacc3e4
commit
9dc489c2b0
@ -482,6 +482,12 @@ pub fn constrain_expr(
|
||||
let and_constraint = constraints.and_constraint(and_cons);
|
||||
constraints.exists(vars, and_constraint)
|
||||
}
|
||||
Expr::Crash => {
|
||||
let crash_type_index = constraints.push_type(Type::Crash);
|
||||
let expected_index = constraints.push_expected_type(expected);
|
||||
|
||||
constraints.equal_types(crash_type_index, expected_index, Category::Crash, region)
|
||||
}
|
||||
Var(symbol, variable) => {
|
||||
// Save the expectation in the variable, then lookup the symbol's type in the environment
|
||||
let expected_type = *constraints[expected].get_type_ref();
|
||||
|
@ -5562,6 +5562,7 @@ pub fn with_hole<'a>(
|
||||
}
|
||||
TypedHole(_) => Stmt::RuntimeError("Hit a blank"),
|
||||
RuntimeError(e) => Stmt::RuntimeError(env.arena.alloc(e.runtime_message())),
|
||||
Crash => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2961,6 +2961,27 @@ fn type_to_variable<'a>(
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
Crash => {
|
||||
let magic_return = subs.fresh(Descriptor {
|
||||
content: Content::FlexVar(None),
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
});
|
||||
let magic_lambda_set = subs.fresh(Descriptor {
|
||||
content: Content::FlexVar(None),
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
});
|
||||
let magic_crash = Content::Structure(FlatType::Func(
|
||||
Subs::STR_SLICE,
|
||||
magic_lambda_set,
|
||||
magic_return,
|
||||
));
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, magic_crash)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -8352,4 +8352,20 @@ mod solve_expr {
|
||||
@"translateStatic : [Element (List a)] as a -[[translateStatic(0)]]-> [Element (List b)]* as b"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_contextual_crash() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [getInfallible] to "./platform"
|
||||
|
||||
getInfallible = \result -> when result is
|
||||
Ok x -> x
|
||||
_ -> crash "turns out this was fallible"
|
||||
"#
|
||||
),
|
||||
"[Ok a]* -> a",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1667,6 +1667,8 @@ impl Subs {
|
||||
pub const TAG_NAME_BAD_UTF_8: SubsIndex<TagName> = SubsIndex::new(3);
|
||||
pub const TAG_NAME_OUT_OF_BOUNDS: SubsIndex<TagName> = SubsIndex::new(4);
|
||||
|
||||
pub const STR_SLICE: VariableSubsSlice = SubsSlice::new(0, 1);
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const AB_ENCODING: SubsSlice<Symbol> = SubsSlice::new(0, 1);
|
||||
#[rustfmt::skip]
|
||||
@ -1704,14 +1706,18 @@ impl Subs {
|
||||
|
||||
let mut subs = Subs {
|
||||
utable: UnificationTable::default(),
|
||||
variables: Vec::new(),
|
||||
variables: vec![
|
||||
// Used for STR_SLICE
|
||||
Variable::STR,
|
||||
],
|
||||
tag_names,
|
||||
symbol_names,
|
||||
field_names: Vec::new(),
|
||||
record_fields: Vec::new(),
|
||||
// store an empty slice at the first position
|
||||
// used for "TagOrFunction"
|
||||
variable_slices: vec![VariableSubsSlice::default()],
|
||||
variable_slices: vec![
|
||||
// used for "TagOrFunction"
|
||||
VariableSubsSlice::default(),
|
||||
],
|
||||
unspecialized_lambda_sets: Vec::new(),
|
||||
tag_name_cache: Default::default(),
|
||||
uls_of_var: Default::default(),
|
||||
|
@ -994,6 +994,7 @@ impl Types {
|
||||
self.set_type_tag(index, TypeTag::RangedNumber(*range), Slice::default())
|
||||
}
|
||||
Type::Error => self.set_type_tag(index, TypeTag::Error, Slice::default()),
|
||||
Type::Crash => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1668,6 +1669,7 @@ pub enum Type {
|
||||
Apply(Symbol, Vec<Loc<Type>>, Region),
|
||||
Variable(Variable),
|
||||
RangedNumber(NumericRange),
|
||||
Crash, // Str -> a
|
||||
/// A type error, which will code gen to a runtime error
|
||||
Error,
|
||||
}
|
||||
@ -1710,6 +1712,7 @@ impl Clone for Type {
|
||||
match self {
|
||||
Self::EmptyRec => Self::EmptyRec,
|
||||
Self::EmptyTagUnion => Self::EmptyTagUnion,
|
||||
Self::Crash => Self::Crash,
|
||||
Self::Function(arg0, arg1, arg2) => {
|
||||
Self::Function(arg0.clone(), arg1.clone(), arg2.clone())
|
||||
}
|
||||
@ -2078,6 +2081,7 @@ impl fmt::Debug for Type {
|
||||
Type::RangedNumber(range_vars) => {
|
||||
write!(f, "Ranged({:?})", range_vars)
|
||||
}
|
||||
Type::Crash => write!(f, "Crash"),
|
||||
Type::UnspecializedLambdaSet { unspecialized } => {
|
||||
write!(f, "{:?}", unspecialized)
|
||||
}
|
||||
@ -2241,7 +2245,7 @@ impl Type {
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Error => {}
|
||||
EmptyRec | EmptyTagUnion | Crash | Error => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2371,7 +2375,7 @@ impl Type {
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Error => {}
|
||||
EmptyRec | EmptyTagUnion | Crash | Error => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2474,7 +2478,7 @@ impl Type {
|
||||
}
|
||||
RangedNumber(_) => Ok(()),
|
||||
UnspecializedLambdaSet { .. } => Ok(()),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => Ok(()),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Crash => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2536,7 +2540,7 @@ impl Type {
|
||||
UnspecializedLambdaSet {
|
||||
unspecialized: Uls(_, sym, _),
|
||||
} => *sym == rep_symbol,
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => false,
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Crash => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2592,7 +2596,7 @@ impl Type {
|
||||
.iter()
|
||||
.any(|arg| arg.value.contains_variable(rep_variable)),
|
||||
RangedNumber(_) => false,
|
||||
EmptyRec | EmptyTagUnion | Error => false,
|
||||
EmptyRec | EmptyTagUnion | Error | Crash => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2966,7 +2970,7 @@ impl Type {
|
||||
}
|
||||
RangedNumber(_) => {}
|
||||
UnspecializedLambdaSet { .. } => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Crash => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3102,7 +3106,7 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
||||
} => {
|
||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||
}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Error | Variable(_) | Crash => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3218,7 +3222,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||
}
|
||||
variables_help(actual, accum);
|
||||
}
|
||||
RangedNumber(_) => {}
|
||||
RangedNumber(_) | Crash => {}
|
||||
Apply(_, args, _) => {
|
||||
for x in args {
|
||||
variables_help(&x.value, accum);
|
||||
@ -3246,7 +3250,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||
use Type::*;
|
||||
|
||||
match tipe {
|
||||
EmptyRec | EmptyTagUnion | Error => (),
|
||||
EmptyRec | EmptyTagUnion | Error | Crash => (),
|
||||
|
||||
Variable(v) => {
|
||||
accum.type_variables.insert(*v);
|
||||
@ -4373,6 +4377,7 @@ fn instantiate_lambda_sets_as_unspecialized(
|
||||
match typ {
|
||||
Type::EmptyRec => {}
|
||||
Type::EmptyTagUnion => {}
|
||||
Type::Crash => {}
|
||||
Type::Function(args, lambda_set, ret) => {
|
||||
debug_assert!(
|
||||
matches!(**lambda_set, Type::Variable(..)),
|
||||
|
@ -1680,6 +1680,10 @@ fn format_category<'b>(
|
||||
alloc.concat([this_is, alloc.text(" an uniqueness attribute")]),
|
||||
alloc.text(" of type:"),
|
||||
),
|
||||
Crash => {
|
||||
internal_error!("calls to crash should be unconditionally admitted in any context, unexpected reachability!");
|
||||
}
|
||||
|
||||
Storage(..) | Unknown => (
|
||||
alloc.concat([this_is, alloc.text(" a value")]),
|
||||
alloc.text(" of type:"),
|
||||
|
@ -12420,4 +12420,59 @@ All branches in an `if` must have the same type!
|
||||
to be more general?
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
crash_given_non_string,
|
||||
indoc!(
|
||||
r#"
|
||||
crash {}
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This 1st argument to this function has an unexpected type:
|
||||
|
||||
4│ crash {}
|
||||
^^
|
||||
|
||||
The argument is a record of type:
|
||||
|
||||
{}
|
||||
|
||||
But this function needs its 1st argument to be:
|
||||
|
||||
Str
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
crash_unapplied,
|
||||
indoc!(
|
||||
r#"
|
||||
crash
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
crash_overapplied,
|
||||
indoc!(
|
||||
r#"
|
||||
crash "" ""
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This function expects 1 argument, but it got 2 instead:
|
||||
|
||||
4│ crash "" ""
|
||||
^^^^^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user