Initial working version of proper try keyword

This commit is contained in:
Sam Mohr 2024-12-02 03:20:44 -08:00
parent a7168a4ad6
commit eedade8e81
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
33 changed files with 1036 additions and 521 deletions

View file

@ -30,8 +30,8 @@ use roc_region::all::{Loc, Region};
use roc_types::subs::{IllegalCycleMark, Variable};
use roc_types::types::Type::{self, *};
use roc_types::types::{
AliasKind, AnnotationSource, Category, IndexOrField, OptAbleType, PReason, Reason, RecordField,
TypeExtension, TypeTag, Types,
AliasKind, AnnotationSource, Category, EarlyReturnKind, IndexOrField, OptAbleType, PReason,
Reason, RecordField, TypeExtension, TypeTag, Types,
};
use soa::{Index, Slice};
@ -152,7 +152,7 @@ fn constrain_untyped_closure(
closure_var: Variable,
ret_var: Variable,
fx_var: Variable,
early_returns: &[(Variable, Region)],
early_returns: &[(Variable, Region, EarlyReturnKind)],
arguments: &[(Variable, AnnotatedMark, Loc<Pattern>)],
loc_body_expr: &Loc<Expr>,
captured_symbols: &[(Symbol, Variable)],
@ -184,30 +184,16 @@ fn constrain_untyped_closure(
));
let returns_constraint = env.with_fx_expectation(fx_var, None, |env| {
let return_con = constrain_expr(
constrain_function_return(
types,
constraints,
env,
loc_body_expr.region,
&loc_body_expr.value,
loc_body_expr,
early_returns,
return_type_index,
);
let mut return_constraints = Vec::with_capacity(early_returns.len() + 1);
return_constraints.push(return_con);
for (early_return_variable, early_return_region) in early_returns {
let early_return_con = constraints.equal_types_var(
*early_return_variable,
return_type_index,
Category::Return,
*early_return_region,
);
return_constraints.push(early_return_con);
}
constraints.and_constraint(return_constraints)
ret_var,
false,
)
});
// make sure the captured symbols are sorted!
@ -259,6 +245,51 @@ fn constrain_untyped_closure(
constraints.exists_many(vars, cons)
}
pub fn constrain_function_return(
types: &mut Types,
constraints: &mut Constraints,
env: &mut Env,
body_expr: &Loc<Expr>,
early_returns: &[(Variable, Region, EarlyReturnKind)],
return_type_expected: ExpectedTypeIndex,
ret_var: Variable,
should_attach_res_constraints: bool,
) -> Constraint {
let return_con = constrain_expr(
types,
constraints,
env,
body_expr.region,
&body_expr.value,
return_type_expected,
);
let mut return_constraints = Vec::with_capacity(early_returns.len() + 1);
let mut return_type_vars = Vec::with_capacity(early_returns.len() + 1);
return_constraints.push(return_con);
return_type_vars.push(ret_var);
for (early_return_variable, early_return_region, early_return_kind) in early_returns {
let early_return_con = constraints.equal_types_var(
*early_return_variable,
return_type_expected,
Category::Return(*early_return_kind),
*early_return_region,
);
return_constraints.push(early_return_con);
return_type_vars.push(*early_return_variable);
}
let returns_constraint = constraints.exists_many(return_type_vars, return_constraints);
if should_attach_res_constraints {
attach_resolution_constraints(constraints, env, returns_constraint)
} else {
returns_constraint
}
}
pub fn constrain_expr(
types: &mut Types,
constraints: &mut Constraints,
@ -576,7 +607,6 @@ pub fn constrain_expr(
let mut arg_cons = Vec::with_capacity(loc_args.len());
for (index, (arg_var, loc_arg)) in loc_args.iter().enumerate() {
let region = loc_arg.region;
let arg_type = Variable(*arg_var);
let arg_type_index = constraints.push_variable(*arg_var);
@ -833,6 +863,78 @@ pub fn constrain_expr(
constraints.exists_many([*variable], [message_con, continuation_con])
}
Try {
result_expr,
result_var,
return_var,
ok_payload_var,
err_payload_var,
err_ext_var,
} => {
let result_var_index = constraints.push_variable(*result_var);
let result_expected_type = constraints.push_expected_type(ForReason(
Reason::TryResult,
result_var_index,
result_expr.region,
));
let result_constraint = constrain_expr(
types,
constraints,
env,
result_expr.region,
&result_expr.value,
result_expected_type,
);
let try_target_constraint = constraints.try_target(
result_var_index,
*ok_payload_var,
*err_payload_var,
result_expr.region,
);
let return_type_index = constraints.push_variable(*return_var);
let expected_return_value = constraints.push_expected_type(ForReason(
Reason::TryResult,
return_type_index,
result_expr.region,
));
let try_failure_type_index = {
let typ = types.from_old_type(&Type::TagUnion(
vec![("Err".into(), vec![Type::Variable(*err_payload_var)])],
TypeExtension::from_non_annotation_type(Type::Variable(*err_ext_var)),
));
constraints.push_type(types, typ)
};
let try_failure_constraint = constraints.equal_types(
try_failure_type_index,
expected_return_value,
Category::TryFailure,
region,
);
let ok_type_index = constraints.push_variable(*ok_payload_var);
let try_success_constraint =
constraints.equal_types(ok_type_index, expected, Category::TrySuccess, region);
constraints.exists_many(
[
*return_var,
*result_var,
*ok_payload_var,
*err_payload_var,
*err_ext_var,
],
[
result_constraint,
try_target_constraint,
try_failure_constraint,
try_success_constraint,
],
)
}
If {
cond_var,
branch_var,
@ -1018,26 +1120,25 @@ pub fn constrain_expr(
Region::span_across(&loc_cond.region, &branches.last().unwrap().value.region)
};
let branch_expr_reason =
|expected: &Expected<TypeOrVar>, index, branch_region| match expected {
FromAnnotation(name, arity, ann_source, _typ) => {
// NOTE deviation from elm.
//
// in elm, `_typ` is used, but because we have this `expr_var` too
// and need to constrain it, this is what works and gives better error messages
FromAnnotation(
name.clone(),
*arity,
AnnotationSource::TypedWhenBranch {
index,
region: ann_source.region(),
},
body_type_index,
)
}
let branch_expr_reason = |expected: &Expected<TypeOrVar>, index| match expected {
FromAnnotation(name, arity, ann_source, _typ) => {
// NOTE deviation from elm.
//
// in elm, `_typ` is used, but because we have this `expr_var` too
// and need to constrain it, this is what works and gives better error messages
FromAnnotation(
name.clone(),
*arity,
AnnotationSource::TypedWhenBranch {
index,
region: ann_source.region(),
},
body_type_index,
)
}
_ => ForReason(Reason::WhenBranch { index }, body_type_index, branch_region),
};
_ => ForReason(Reason::WhenBranch { index }, body_type_index, region),
};
// Our goal is to constrain and introduce variables in all pattern when branch patterns before
// looking at their bodies.
@ -1091,11 +1192,7 @@ pub fn constrain_expr(
region,
when_branch,
expected_pattern,
branch_expr_reason(
&constraints[expected],
HumanIndex::zero_based(index),
when_branch.value.region,
),
branch_expr_reason(&constraints[expected], HumanIndex::zero_based(index)),
);
pattern_vars.extend(new_pattern_vars);
@ -2074,43 +2171,19 @@ fn constrain_function_def(
constraints.push_type(types, fn_type)
};
let returns_constraint = {
let return_con = constrain_expr(
types,
constraints,
env,
loc_body_expr.region,
&loc_body_expr.value,
return_type_annotation_expected,
);
let mut return_constraints =
Vec::with_capacity(function_def.early_returns.len() + 1);
return_constraints.push(return_con);
for (early_return_variable, early_return_region) in &function_def.early_returns {
let early_return_type_expected =
constraints.push_expected_type(Expected::ForReason(
Reason::FunctionOutput,
ret_type_index,
*early_return_region,
));
vars.push(*early_return_variable);
let early_return_con = constraints.equal_types_var(
*early_return_variable,
early_return_type_expected,
Category::Return,
*early_return_region,
);
return_constraints.push(early_return_con);
}
let returns_constraint = constraints.and_constraint(return_constraints);
attach_resolution_constraints(constraints, env, returns_constraint)
};
let returns_constraint =
env.with_fx_expectation(function_def.fx_type, Some(annotation.region), |env| {
constrain_function_return(
types,
constraints,
env,
loc_body_expr,
&function_def.early_returns,
return_type_annotation_expected,
function_def.return_type,
true,
)
});
vars.push(expr_var);
@ -2459,7 +2532,7 @@ fn constrain_when_branch_help(
types,
constraints,
env,
region,
when_branch.value.region,
&when_branch.value.value,
expr_expected,
);
@ -2966,32 +3039,16 @@ fn constrain_typed_def(
let returns_constraint =
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
let return_con = constrain_expr(
constrain_function_return(
types,
constraints,
env,
loc_body_expr.region,
&loc_body_expr.value,
loc_body_expr,
early_returns,
return_type,
);
let mut return_constraints = Vec::with_capacity(early_returns.len() + 1);
return_constraints.push(return_con);
for (early_return_variable, early_return_region) in early_returns {
let early_return_con = constraints.equal_types_var(
*early_return_variable,
return_type,
Category::Return,
*early_return_region,
);
return_constraints.push(early_return_con);
}
let returns_constraint = constraints.and_constraint(return_constraints);
attach_resolution_constraints(constraints, env, returns_constraint)
ret_var,
true,
)
});
vars.push(*fn_var);
@ -4025,35 +4082,22 @@ fn constraint_recursive_function(
let returns_constraint =
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
let expected = constraints.push_expected_type(NoExpectation(ret_type_index));
let return_con = constrain_expr(
let expected = constraints.push_expected_type(ForReason(
Reason::FunctionOutput,
ret_type_index,
region,
));
constrain_function_return(
types,
constraints,
env,
loc_body_expr.region,
&loc_body_expr.value,
loc_body_expr,
&function_def.early_returns,
expected,
);
let mut return_constraints =
Vec::with_capacity(function_def.early_returns.len() + 1);
return_constraints.push(return_con);
for (early_return_variable, early_return_region) in &function_def.early_returns
{
let early_return_con = constraints.equal_types_var(
*early_return_variable,
expected,
Category::Return,
*early_return_region,
);
return_constraints.push(early_return_con);
}
let returns_constraint = constraints.and_constraint(return_constraints);
attach_resolution_constraints(constraints, env, returns_constraint)
ret_var,
true,
)
});
vars.push(expr_var);
@ -4406,6 +4450,7 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
| RecordUpdate { .. }
| Expect { .. }
| Dbg { .. }
| Try { .. }
| Return { .. }
| RuntimeError(..)
| ZeroArgumentTag { .. }
@ -4597,37 +4642,20 @@ fn rec_defs_help(
};
let returns_constraint =
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
let return_type_expected =
constraints.push_expected_type(NoExpectation(ret_type_index));
let return_type_expected = constraints.push_expected_type(
ForReason(Reason::FunctionOutput, ret_type_index, region),
);
let return_con = constrain_expr(
constrain_function_return(
types,
constraints,
env,
loc_body_expr.region,
&loc_body_expr.value,
loc_body_expr,
early_returns,
return_type_expected,
);
let mut return_constraints =
Vec::with_capacity(early_returns.len() + 1);
return_constraints.push(return_con);
for (early_return_variable, early_return_region) in early_returns {
let early_return_con = constraints.equal_types_var(
*early_return_variable,
return_type_expected,
Category::Return,
*early_return_region,
);
return_constraints.push(early_return_con);
}
let returns_constraint =
constraints.and_constraint(return_constraints);
attach_resolution_constraints(constraints, env, returns_constraint)
ret_var,
true,
)
});
vars.push(*fn_var);