mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
improved error messages for function definitions
This commit is contained in:
parent
1d2251b064
commit
e93c04a8ce
4 changed files with 267 additions and 79 deletions
|
@ -307,7 +307,7 @@ pub fn constrain_expr(
|
|||
}
|
||||
Var(symbol) => Lookup(*symbol, expected, region),
|
||||
Closure(fn_var, _symbol, _recursive, args, boxed) => {
|
||||
let (loc_body_expr, ret_var) = &**boxed;
|
||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(args.len()),
|
||||
|
@ -1001,13 +1001,18 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
&mut pattern_state.headers,
|
||||
);
|
||||
|
||||
let env = &Env {
|
||||
home: env.home,
|
||||
rigids: ftv,
|
||||
};
|
||||
|
||||
let annotation_expected = FromAnnotation(
|
||||
def.loc_pattern.clone(),
|
||||
arity,
|
||||
AnnotationSource::TypedBody {
|
||||
region: annotation.region,
|
||||
},
|
||||
signature,
|
||||
signature.clone(),
|
||||
);
|
||||
|
||||
pattern_state.constraints.push(Eq(
|
||||
|
@ -1017,15 +1022,118 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
annotation.region,
|
||||
));
|
||||
|
||||
constrain_expr(
|
||||
&Env {
|
||||
home: env.home,
|
||||
rigids: ftv,
|
||||
},
|
||||
def.loc_expr.region,
|
||||
&def.loc_expr.value,
|
||||
annotation_expected,
|
||||
)
|
||||
// when a def is annotated, and it's body is a closure, treat this
|
||||
// as a named function (in elm terms) for error messages.
|
||||
//
|
||||
// This means we get errors like "the first argument of `f` is weird"
|
||||
// instead of the more generic "something is wrong with the body of `f`"
|
||||
match (&def.loc_expr.value, &signature) {
|
||||
(
|
||||
Closure(fn_var, _symbol, _recursive, args, boxed),
|
||||
Type::Function(arg_types, _),
|
||||
) if true => {
|
||||
let expected = annotation_expected;
|
||||
let region = def.loc_expr.region;
|
||||
|
||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(args.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
let ret_var = *ret_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
|
||||
vars.push(ret_var);
|
||||
|
||||
let it = args.iter().zip(arg_types.iter()).enumerate();
|
||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
||||
{
|
||||
// ensure type matches the one in the annotation
|
||||
let opt_label =
|
||||
if let Pattern::Identifier(label) = def.loc_pattern.value {
|
||||
Some(label)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let pattern_type: &Type = loc_ann;
|
||||
|
||||
let pattern_expected = PExpected::ForReason(
|
||||
PReason::TypedArg {
|
||||
index: Index::zero_based(index),
|
||||
opt_name: opt_label,
|
||||
},
|
||||
pattern_type.clone(),
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
constrain_pattern(
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
pattern_expected,
|
||||
&mut state,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// record the correct type in pattern_var
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
pattern_types.push(pattern_type.clone());
|
||||
|
||||
state.vars.push(*pattern_var);
|
||||
state.constraints.push(Constraint::Eq(
|
||||
pattern_type.clone(),
|
||||
Expected::NoExpectation(loc_ann.clone()),
|
||||
Category::Storage,
|
||||
loc_pattern.region,
|
||||
));
|
||||
|
||||
vars.push(*pattern_var);
|
||||
}
|
||||
}
|
||||
|
||||
let fn_type = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
||||
let body_type = NoExpectation(ret_type);
|
||||
let ret_constraint =
|
||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||
|
||||
vars.push(*fn_var);
|
||||
let defs_constraint = And(state.constraints);
|
||||
|
||||
exists(
|
||||
vars,
|
||||
And(vec![
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: state.vars,
|
||||
def_types: state.headers,
|
||||
def_aliases: SendMap::default(),
|
||||
defs_constraint,
|
||||
ret_constraint,
|
||||
})),
|
||||
// "the closure's type is equal to expected type"
|
||||
Eq(fn_type.clone(), expected, Category::Lambda, region),
|
||||
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||
Eq(
|
||||
Type::Variable(*fn_var),
|
||||
NoExpectation(fn_type),
|
||||
Category::Storage,
|
||||
region,
|
||||
),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
_ => constrain_expr(
|
||||
&env,
|
||||
def.loc_expr.region,
|
||||
&def.loc_expr.value,
|
||||
annotation_expected,
|
||||
),
|
||||
}
|
||||
}
|
||||
None => constrain_expr(
|
||||
env,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue