mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-31 00:57:24 +00:00
Restrict usages of type variables in non-generalized contexts
Type variables can only be used on functions (and in number literals as a carve-out for now). In all other cases, a type variable takes on a single, concrete type based on later usages. This check emits errors when this is violated. The implementation is to check the rank of a variable after it could be generalized. If the variable is not generalized but annotated as a type variable, emit an error.
This commit is contained in:
parent
f5961cbb22
commit
a0461679dd
13 changed files with 230 additions and 114 deletions
|
@ -15,15 +15,12 @@ use bumpalo::Bump;
|
|||
use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::{
|
||||
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, LetConstraint, OpportunisticResolve,
|
||||
TryTargetConstraint,
|
||||
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, Generalizable, LetConstraint,
|
||||
OpportunisticResolve, TryTargetConstraint,
|
||||
};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::module::ModuleParams;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::IdentSuffix;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
|
@ -32,8 +29,8 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_solve_problem::TypeError;
|
||||
use roc_solve_schema::UnificationMode;
|
||||
use roc_types::subs::{
|
||||
self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar,
|
||||
Variable,
|
||||
self, Content, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt,
|
||||
UlsOfVar, Variable,
|
||||
};
|
||||
use roc_types::types::{Category, Polarity, Reason, RecordField, Type, TypeExtension, Types, Uls};
|
||||
use roc_unify::unify::{
|
||||
|
@ -356,29 +353,13 @@ fn solve(
|
|||
generalize(env, young_mark, visit_mark, rank.next());
|
||||
debug_assert!(env.pools.get(rank.next()).is_empty(), "variables left over in let-binding scope, but they should all be in a lower scope or generalized now");
|
||||
|
||||
// check that things went well
|
||||
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
|
||||
let rigid_vars = &env.constraints[let_con.rigid_vars];
|
||||
|
||||
// NOTE the `subs.redundant` check does not come from elm.
|
||||
// It's unclear whether this is a bug with our implementation
|
||||
// (something is redundant that shouldn't be)
|
||||
// or that it just never came up in elm.
|
||||
let mut it = rigid_vars
|
||||
.iter()
|
||||
.filter(|loc_var| {
|
||||
let var = loc_var.value;
|
||||
!env.subs.redundant(var) && env.subs.get_rank(var) != Rank::GENERALIZED
|
||||
})
|
||||
.peekable();
|
||||
|
||||
if it.peek().is_some() {
|
||||
let failing: Vec<_> = it.collect();
|
||||
println!("Rigids {:?}", &rigid_vars);
|
||||
println!("Failing {failing:?}");
|
||||
debug_assert!(false);
|
||||
}
|
||||
});
|
||||
let named_variables = &env.constraints[let_con.rigid_vars];
|
||||
check_named_variables_are_generalized(
|
||||
env,
|
||||
problems,
|
||||
named_variables,
|
||||
let_con.generalizable,
|
||||
);
|
||||
|
||||
let mut new_scope = scope.clone();
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
|
@ -1636,6 +1617,30 @@ fn solve(
|
|||
state
|
||||
}
|
||||
|
||||
fn check_named_variables_are_generalized(
|
||||
env: &mut InferenceEnv<'_>,
|
||||
problems: &mut Vec<TypeError>,
|
||||
named_variables: &[Loc<Variable>],
|
||||
generalizable: Generalizable,
|
||||
) {
|
||||
for loc_var in named_variables {
|
||||
let is_generalized = env.subs.get_rank(loc_var.value) == Rank::GENERALIZED;
|
||||
if !is_generalized {
|
||||
// TODO: should be OF_PATTERN if on the LHS of a function, otherwise OF_VALUE.
|
||||
let polarity = Polarity::OF_VALUE;
|
||||
let ctx = ErrorTypeContext::NON_GENERALIZED_AS_INFERRED;
|
||||
let error_type = env
|
||||
.subs
|
||||
.var_to_error_type_contextual(loc_var.value, ctx, polarity);
|
||||
problems.push(TypeError::TypeIsNotGeneralized(
|
||||
loc_var.region,
|
||||
error_type,
|
||||
generalizable,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_suffix_fx(
|
||||
env: &mut InferenceEnv<'_>,
|
||||
problems: &mut Vec<TypeError>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue