mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +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
|
@ -22,3 +22,4 @@ bumpalo.workspace = true
|
|||
static_assertions.workspace = true
|
||||
|
||||
soa.workspace = true
|
||||
bitflags.workspace = true
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::types::{
|
|||
Polarity, RecordField, RecordFieldsError, TupleElemsError, TypeExt, Uls,
|
||||
};
|
||||
use crate::unification_table::{self, UnificationTable};
|
||||
use bitflags::bitflags;
|
||||
use roc_collections::all::{FnvMap, ImMap, ImSet, MutSet, SendMap};
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
|
@ -50,10 +51,24 @@ impl fmt::Debug for Mark {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ErrorTypeContext {
|
||||
None,
|
||||
ExpandRanges,
|
||||
bitflags! {
|
||||
pub struct ErrorTypeContext : u8 {
|
||||
const NONE = 1 << 0;
|
||||
/// List all number types that satisfy number range constraints.
|
||||
const EXPAND_RANGES = 1 << 1;
|
||||
/// Re-write non-generalized types like to inference variables.
|
||||
const NON_GENERALIZED_AS_INFERRED = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorTypeContext {
|
||||
fn expand_ranges(&self) -> bool {
|
||||
self.contains(Self::EXPAND_RANGES)
|
||||
}
|
||||
|
||||
fn non_generalized_as_inferred(&self) -> bool {
|
||||
self.contains(Self::NON_GENERALIZED_AS_INFERRED)
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorTypeState {
|
||||
|
@ -2055,7 +2070,7 @@ impl Subs {
|
|||
}
|
||||
|
||||
pub fn var_to_error_type(&mut self, var: Variable, observed_pol: Polarity) -> ErrorType {
|
||||
self.var_to_error_type_contextual(var, ErrorTypeContext::None, observed_pol)
|
||||
self.var_to_error_type_contextual(var, ErrorTypeContext::empty(), observed_pol)
|
||||
}
|
||||
|
||||
pub fn var_to_error_type_contextual(
|
||||
|
@ -4020,6 +4035,13 @@ fn content_to_err_type(
|
|||
match content {
|
||||
Structure(flat_type) => flat_type_to_err_type(subs, state, flat_type, pol),
|
||||
|
||||
RigidVar(..) | RigidAbleVar(..)
|
||||
if state.context.non_generalized_as_inferred()
|
||||
&& subs.get_rank(var) != Rank::GENERALIZED =>
|
||||
{
|
||||
ErrorType::InferenceVar
|
||||
}
|
||||
|
||||
FlexVar(opt_name) => {
|
||||
let name = match opt_name {
|
||||
Some(name_index) => subs.field_names[name_index.index()].clone(),
|
||||
|
@ -4123,7 +4145,7 @@ fn content_to_err_type(
|
|||
}
|
||||
|
||||
RangedNumber(range) => {
|
||||
if state.context == ErrorTypeContext::ExpandRanges {
|
||||
if state.context.expand_ranges() {
|
||||
let mut types = Vec::new();
|
||||
for var in range.variable_slice() {
|
||||
types.push(var_to_err_type(subs, state, *var, pol));
|
||||
|
|
|
@ -3679,6 +3679,7 @@ pub enum ErrorType {
|
|||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexVar(Lowercase),
|
||||
RigidVar(Lowercase),
|
||||
InferenceVar,
|
||||
EffectfulFunc,
|
||||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexAbleVar(Lowercase, AbilitySet),
|
||||
|
@ -3733,6 +3734,7 @@ impl ErrorType {
|
|||
FlexVar(v) | RigidVar(v) | FlexAbleVar(v, _) | RigidAbleVar(v, _) => {
|
||||
taken.insert(v.clone());
|
||||
}
|
||||
InferenceVar => {}
|
||||
Record(fields, ext) => {
|
||||
fields
|
||||
.iter()
|
||||
|
@ -3912,13 +3914,14 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
Infinite => buf.push('∞'),
|
||||
Error => buf.push('?'),
|
||||
FlexVar(name) | RigidVar(name) => buf.push_str(name.as_str()),
|
||||
FlexAbleVar(name, symbol) | RigidAbleVar(name, symbol) => {
|
||||
InferenceVar => buf.push('_'),
|
||||
FlexAbleVar(name, abilities) | RigidAbleVar(name, abilities) => {
|
||||
let write_parens = parens == Parens::InTypeParam;
|
||||
if write_parens {
|
||||
buf.push('(');
|
||||
}
|
||||
buf.push_str(name.as_str());
|
||||
write!(buf, "{} {:?}", roc_parse::keyword::IMPLEMENTS, symbol).unwrap();
|
||||
write!(buf, "{} {:?}", roc_parse::keyword::IMPLEMENTS, abilities).unwrap();
|
||||
if write_parens {
|
||||
buf.push(')');
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue