mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
Merge remote-tracking branch 'origin/trunk' into instantiate-rigids-speedup-again
This commit is contained in:
commit
e0aa246c5e
3
AUTHORS
3
AUTHORS
@ -65,3 +65,6 @@ Mats Sigge <<mats.sigge@gmail.com>>
|
||||
Drew Lazzeri <dlazzeri1@gmail.com>
|
||||
Tom Dohrmann <erbse.13@gmx.de>
|
||||
Elijah Schow <elijah.schow@gmail.com>
|
||||
Derek Gustafson <degustaf@gmail.com>
|
||||
Philippe Vinchon <p.vinchon@gmail.com>
|
||||
Pierre-Henri Trivier <pierre-henri.trivier@easymile.com>
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3357,6 +3357,7 @@ dependencies = [
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_types",
|
||||
"static_assertions",
|
||||
"ven_graph",
|
||||
]
|
||||
|
||||
@ -3426,6 +3427,7 @@ dependencies = [
|
||||
name = "roc_constrain"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_collections::all::{BumpMap, BumpMapDefault, Index, SendMap};
|
||||
use roc_collections::all::{BumpMap, BumpMapDefault, HumanIndex, SendMap};
|
||||
use roc_module::{
|
||||
ident::{Lowercase, TagName},
|
||||
symbol::Symbol,
|
||||
@ -163,7 +163,7 @@ pub fn constrain_expr<'a>(
|
||||
|
||||
let elem_expected = Expected::ForReason(
|
||||
Reason::ElemInList {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
list_elem_type.shallow_clone(),
|
||||
region,
|
||||
@ -339,7 +339,7 @@ pub fn constrain_expr<'a>(
|
||||
|
||||
let reason = Reason::FnArg {
|
||||
name: opt_symbol,
|
||||
arg_index: Index::zero_based(index),
|
||||
arg_index: HumanIndex::zero_based(index),
|
||||
};
|
||||
|
||||
let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region);
|
||||
@ -538,7 +538,7 @@ pub fn constrain_expr<'a>(
|
||||
name.clone(),
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
num_branches,
|
||||
region: ann_source.region(),
|
||||
},
|
||||
@ -559,7 +559,7 @@ pub fn constrain_expr<'a>(
|
||||
name,
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(branches.len()),
|
||||
index: HumanIndex::zero_based(branches.len()),
|
||||
num_branches,
|
||||
region: ann_source.region(),
|
||||
},
|
||||
@ -596,7 +596,7 @@ pub fn constrain_expr<'a>(
|
||||
body,
|
||||
Expected::ForReason(
|
||||
Reason::IfBranch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
total_branches: branches.len(),
|
||||
},
|
||||
Type2::Variable(*expr_var),
|
||||
@ -616,7 +616,7 @@ pub fn constrain_expr<'a>(
|
||||
final_else_expr,
|
||||
Expected::ForReason(
|
||||
Reason::IfBranch {
|
||||
index: Index::zero_based(branches.len()),
|
||||
index: HumanIndex::zero_based(branches.len()),
|
||||
total_branches: branches.len() + 1,
|
||||
},
|
||||
Type2::Variable(*expr_var),
|
||||
@ -691,7 +691,7 @@ pub fn constrain_expr<'a>(
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.shallow_clone(),
|
||||
pattern_region,
|
||||
@ -700,7 +700,7 @@ pub fn constrain_expr<'a>(
|
||||
name.clone(),
|
||||
*arity,
|
||||
AnnotationSource::TypedWhenBranch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
region: ann_source.region(),
|
||||
},
|
||||
typ.shallow_clone(),
|
||||
@ -733,14 +733,14 @@ pub fn constrain_expr<'a>(
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
cond_type.shallow_clone(),
|
||||
pattern_region,
|
||||
),
|
||||
Expected::ForReason(
|
||||
Reason::WhenBranch {
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
branch_type.shallow_clone(),
|
||||
// TODO: when_branch.value.region,
|
||||
@ -1065,7 +1065,7 @@ pub fn constrain_expr<'a>(
|
||||
|
||||
let reason = Reason::LowLevelOpArg {
|
||||
op: *op,
|
||||
arg_index: Index::zero_based(index),
|
||||
arg_index: HumanIndex::zero_based(index),
|
||||
};
|
||||
let expected_arg =
|
||||
Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero());
|
||||
@ -1681,7 +1681,7 @@ fn constrain_tag_pattern<'a>(
|
||||
let expected = PExpected::ForReason(
|
||||
PReason::TagArg {
|
||||
tag_name: tag_name.clone(),
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
pattern_type,
|
||||
region,
|
||||
|
@ -16,6 +16,7 @@ roc_types = { path = "../types" }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
ven_graph = { path = "../../vendor/pathfinding" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0.0"
|
||||
|
@ -1,175 +1,483 @@
|
||||
use crate::expected::{Expected, PExpected};
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_module::{ident::TagName, symbol::Symbol};
|
||||
use roc_collections::soa::{Index, Slice};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{Category, PatternCategory, Type};
|
||||
use roc_types::{subs::Variable, types::VariableDetail};
|
||||
|
||||
/// A presence constraint is an additive constraint that defines the lower bound
|
||||
/// of a type. For example, `Present(t1, IncludesTag(A, []))` means that the
|
||||
/// type `t1` must contain at least the tag `A`. The additive nature of these
|
||||
/// constraints makes them behaviorally different from unification-based constraints.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PresenceConstraint {
|
||||
IncludesTag(TagName, Vec<Type>, Region, PatternCategory),
|
||||
IsOpen,
|
||||
Pattern(Region, PatternCategory, PExpected<Type>),
|
||||
#[derive(Debug)]
|
||||
pub struct Constraints {
|
||||
pub constraints: Vec<Constraint>,
|
||||
pub types: Vec<Type>,
|
||||
pub variables: Vec<Variable>,
|
||||
pub def_types: Vec<(Symbol, Loc<Index<Type>>)>,
|
||||
pub let_constraints: Vec<LetConstraint>,
|
||||
pub categories: Vec<Category>,
|
||||
pub pattern_categories: Vec<PatternCategory>,
|
||||
pub expectations: Vec<Expected<Type>>,
|
||||
pub pattern_expectations: Vec<PExpected<Type>>,
|
||||
pub includes_tags: Vec<IncludesTag>,
|
||||
pub strings: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Default for Constraints {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Constraints {
|
||||
pub fn new() -> Self {
|
||||
let constraints = Vec::new();
|
||||
let mut types = Vec::new();
|
||||
let variables = Vec::new();
|
||||
let def_types = Vec::new();
|
||||
let let_constraints = Vec::new();
|
||||
let mut categories = Vec::with_capacity(16);
|
||||
let mut pattern_categories = Vec::with_capacity(16);
|
||||
let expectations = Vec::new();
|
||||
let pattern_expectations = Vec::new();
|
||||
let includes_tags = Vec::new();
|
||||
let strings = Vec::new();
|
||||
|
||||
types.extend([Type::EmptyRec, Type::EmptyTagUnion]);
|
||||
|
||||
categories.extend([
|
||||
Category::Record,
|
||||
Category::ForeignCall,
|
||||
Category::OpaqueArg,
|
||||
Category::Lambda,
|
||||
Category::ClosureSize,
|
||||
Category::StrInterpolation,
|
||||
Category::If,
|
||||
Category::When,
|
||||
Category::Float,
|
||||
Category::Int,
|
||||
Category::Num,
|
||||
Category::List,
|
||||
Category::Str,
|
||||
Category::Character,
|
||||
]);
|
||||
|
||||
pattern_categories.extend([
|
||||
PatternCategory::Record,
|
||||
PatternCategory::EmptyRecord,
|
||||
PatternCategory::PatternGuard,
|
||||
PatternCategory::PatternDefault,
|
||||
PatternCategory::Set,
|
||||
PatternCategory::Map,
|
||||
PatternCategory::Str,
|
||||
PatternCategory::Num,
|
||||
PatternCategory::Int,
|
||||
PatternCategory::Float,
|
||||
PatternCategory::Character,
|
||||
]);
|
||||
|
||||
Self {
|
||||
constraints,
|
||||
types,
|
||||
variables,
|
||||
def_types,
|
||||
let_constraints,
|
||||
categories,
|
||||
pattern_categories,
|
||||
expectations,
|
||||
pattern_expectations,
|
||||
includes_tags,
|
||||
strings,
|
||||
}
|
||||
}
|
||||
|
||||
pub const EMPTY_RECORD: Index<Type> = Index::new(0);
|
||||
pub const EMPTY_TAG_UNION: Index<Type> = Index::new(1);
|
||||
|
||||
pub const CATEGORY_RECORD: Index<Category> = Index::new(0);
|
||||
pub const CATEGORY_FOREIGNCALL: Index<Category> = Index::new(1);
|
||||
pub const CATEGORY_OPAQUEARG: Index<Category> = Index::new(2);
|
||||
pub const CATEGORY_LAMBDA: Index<Category> = Index::new(3);
|
||||
pub const CATEGORY_CLOSURESIZE: Index<Category> = Index::new(4);
|
||||
pub const CATEGORY_STRINTERPOLATION: Index<Category> = Index::new(5);
|
||||
pub const CATEGORY_IF: Index<Category> = Index::new(6);
|
||||
pub const CATEGORY_WHEN: Index<Category> = Index::new(7);
|
||||
pub const CATEGORY_FLOAT: Index<Category> = Index::new(8);
|
||||
pub const CATEGORY_INT: Index<Category> = Index::new(9);
|
||||
pub const CATEGORY_NUM: Index<Category> = Index::new(10);
|
||||
pub const CATEGORY_LIST: Index<Category> = Index::new(11);
|
||||
pub const CATEGORY_STR: Index<Category> = Index::new(12);
|
||||
pub const CATEGORY_CHARACTER: Index<Category> = Index::new(13);
|
||||
|
||||
pub const PCATEGORY_RECORD: Index<PatternCategory> = Index::new(0);
|
||||
pub const PCATEGORY_EMPTYRECORD: Index<PatternCategory> = Index::new(1);
|
||||
pub const PCATEGORY_PATTERNGUARD: Index<PatternCategory> = Index::new(2);
|
||||
pub const PCATEGORY_PATTERNDEFAULT: Index<PatternCategory> = Index::new(3);
|
||||
pub const PCATEGORY_SET: Index<PatternCategory> = Index::new(4);
|
||||
pub const PCATEGORY_MAP: Index<PatternCategory> = Index::new(5);
|
||||
pub const PCATEGORY_STR: Index<PatternCategory> = Index::new(6);
|
||||
pub const PCATEGORY_NUM: Index<PatternCategory> = Index::new(7);
|
||||
pub const PCATEGORY_INT: Index<PatternCategory> = Index::new(8);
|
||||
pub const PCATEGORY_FLOAT: Index<PatternCategory> = Index::new(9);
|
||||
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_type(&mut self, typ: Type) -> Index<Type> {
|
||||
match typ {
|
||||
Type::EmptyRec => Self::EMPTY_RECORD,
|
||||
Type::EmptyTagUnion => Self::EMPTY_TAG_UNION,
|
||||
other => Index::push_new(&mut self.types, other),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
|
||||
Index::push_new(&mut self.expectations, expected)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_category(&mut self, category: Category) -> Index<Category> {
|
||||
match category {
|
||||
Category::Record => Self::CATEGORY_RECORD,
|
||||
Category::ForeignCall => Self::CATEGORY_FOREIGNCALL,
|
||||
Category::OpaqueArg => Self::CATEGORY_OPAQUEARG,
|
||||
Category::Lambda => Self::CATEGORY_LAMBDA,
|
||||
Category::ClosureSize => Self::CATEGORY_CLOSURESIZE,
|
||||
Category::StrInterpolation => Self::CATEGORY_STRINTERPOLATION,
|
||||
Category::If => Self::CATEGORY_IF,
|
||||
Category::When => Self::CATEGORY_WHEN,
|
||||
Category::Float => Self::CATEGORY_FLOAT,
|
||||
Category::Int => Self::CATEGORY_INT,
|
||||
Category::Num => Self::CATEGORY_NUM,
|
||||
Category::List => Self::CATEGORY_LIST,
|
||||
Category::Str => Self::CATEGORY_STR,
|
||||
Category::Character => Self::CATEGORY_CHARACTER,
|
||||
other => Index::push_new(&mut self.categories, other),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push_pattern_category(&mut self, category: PatternCategory) -> Index<PatternCategory> {
|
||||
match category {
|
||||
PatternCategory::Record => Self::PCATEGORY_RECORD,
|
||||
PatternCategory::EmptyRecord => Self::PCATEGORY_EMPTYRECORD,
|
||||
PatternCategory::PatternGuard => Self::PCATEGORY_PATTERNGUARD,
|
||||
PatternCategory::PatternDefault => Self::PCATEGORY_PATTERNDEFAULT,
|
||||
PatternCategory::Set => Self::PCATEGORY_SET,
|
||||
PatternCategory::Map => Self::PCATEGORY_MAP,
|
||||
PatternCategory::Str => Self::PCATEGORY_STR,
|
||||
PatternCategory::Num => Self::PCATEGORY_NUM,
|
||||
PatternCategory::Int => Self::PCATEGORY_INT,
|
||||
PatternCategory::Float => Self::PCATEGORY_FLOAT,
|
||||
PatternCategory::Character => Self::PCATEGORY_CHARACTER,
|
||||
other => Index::push_new(&mut self.pattern_categories, other),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal_types(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
expected: Expected<Type>,
|
||||
category: Category,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
let expected_index = Index::push_new(&mut self.expectations, expected);
|
||||
let category_index = Self::push_category(self, category);
|
||||
|
||||
Constraint::Eq(type_index, expected_index, category_index, region)
|
||||
}
|
||||
|
||||
pub fn equal_pattern_types(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
expected: PExpected<Type>,
|
||||
category: PatternCategory,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||
let category_index = Self::push_pattern_category(self, category);
|
||||
|
||||
Constraint::Pattern(type_index, expected_index, category_index, region)
|
||||
}
|
||||
|
||||
pub fn pattern_presence(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
expected: PExpected<Type>,
|
||||
category: PatternCategory,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
||||
|
||||
Constraint::PatternPresence(type_index, expected_index, category_index, region)
|
||||
}
|
||||
|
||||
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
|
||||
Constraint::IsOpenType(type_index)
|
||||
}
|
||||
|
||||
pub fn includes_tag<I>(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
tag_name: TagName,
|
||||
types: I,
|
||||
category: PatternCategory,
|
||||
region: Region,
|
||||
) -> Constraint
|
||||
where
|
||||
I: IntoIterator<Item = Type>,
|
||||
{
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
||||
let types_slice = Slice::extend_new(&mut self.types, types);
|
||||
|
||||
let includes_tag = IncludesTag {
|
||||
type_index,
|
||||
tag_name,
|
||||
types: types_slice,
|
||||
pattern_category: category_index,
|
||||
region,
|
||||
};
|
||||
|
||||
let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag);
|
||||
|
||||
Constraint::IncludesTag(includes_tag_index)
|
||||
}
|
||||
|
||||
fn variable_slice<I>(&mut self, it: I) -> Slice<Variable>
|
||||
where
|
||||
I: IntoIterator<Item = Variable>,
|
||||
{
|
||||
let start = self.variables.len();
|
||||
self.variables.extend(it);
|
||||
let length = self.variables.len() - start;
|
||||
|
||||
Slice::new(start as _, length as _)
|
||||
}
|
||||
|
||||
fn def_types_slice<I>(&mut self, it: I) -> Slice<(Symbol, Loc<Type>)>
|
||||
where
|
||||
I: IntoIterator<Item = (Symbol, Loc<Type>)>,
|
||||
{
|
||||
let start = self.def_types.len();
|
||||
|
||||
for (symbol, loc_type) in it {
|
||||
let type_index = Index::new(self.types.len() as _);
|
||||
let Loc { region, value } = loc_type;
|
||||
self.types.push(value);
|
||||
|
||||
self.def_types.push((symbol, Loc::at(region, type_index)));
|
||||
}
|
||||
|
||||
let length = self.def_types.len() - start;
|
||||
|
||||
Slice::new(start as _, length as _)
|
||||
}
|
||||
|
||||
pub fn exists<I>(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint
|
||||
where
|
||||
I: IntoIterator<Item = Variable>,
|
||||
{
|
||||
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||
|
||||
self.constraints.push(defs_constraint);
|
||||
self.constraints.push(Constraint::True);
|
||||
|
||||
let let_contraint = LetConstraint {
|
||||
rigid_vars: Slice::default(),
|
||||
flex_vars: self.variable_slice(flex_vars),
|
||||
def_types: Slice::default(),
|
||||
defs_and_ret_constraint,
|
||||
};
|
||||
|
||||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
}
|
||||
|
||||
pub fn exists_many<I, C>(&mut self, flex_vars: I, defs_constraint: C) -> Constraint
|
||||
where
|
||||
I: IntoIterator<Item = Variable>,
|
||||
C: IntoIterator<Item = Constraint>,
|
||||
C::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let defs_constraint = self.and_constraint(defs_constraint);
|
||||
|
||||
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||
self.constraints.push(defs_constraint);
|
||||
self.constraints.push(Constraint::True);
|
||||
|
||||
let let_contraint = LetConstraint {
|
||||
rigid_vars: Slice::default(),
|
||||
flex_vars: self.variable_slice(flex_vars),
|
||||
def_types: Slice::default(),
|
||||
defs_and_ret_constraint,
|
||||
};
|
||||
|
||||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
}
|
||||
|
||||
pub fn let_constraint<I1, I2, I3>(
|
||||
&mut self,
|
||||
rigid_vars: I1,
|
||||
flex_vars: I2,
|
||||
def_types: I3,
|
||||
defs_constraint: Constraint,
|
||||
ret_constraint: Constraint,
|
||||
) -> Constraint
|
||||
where
|
||||
I1: IntoIterator<Item = Variable>,
|
||||
I2: IntoIterator<Item = Variable>,
|
||||
I3: IntoIterator<Item = (Symbol, Loc<Type>)>,
|
||||
{
|
||||
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||
|
||||
self.constraints.push(defs_constraint);
|
||||
self.constraints.push(ret_constraint);
|
||||
|
||||
let let_contraint = LetConstraint {
|
||||
rigid_vars: self.variable_slice(rigid_vars),
|
||||
flex_vars: self.variable_slice(flex_vars),
|
||||
def_types: self.def_types_slice(def_types),
|
||||
defs_and_ret_constraint,
|
||||
};
|
||||
|
||||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
}
|
||||
|
||||
pub fn and_constraint<I>(&mut self, constraints: I) -> Constraint
|
||||
where
|
||||
I: IntoIterator<Item = Constraint>,
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let mut it = constraints.into_iter();
|
||||
|
||||
match it.len() {
|
||||
0 => Constraint::True,
|
||||
1 => it.next().unwrap(),
|
||||
_ => {
|
||||
let start = self.constraints.len() as u32;
|
||||
|
||||
self.constraints.extend(it);
|
||||
|
||||
let end = self.constraints.len() as u32;
|
||||
|
||||
let slice = Slice::new(start, (end - start) as u16);
|
||||
|
||||
Constraint::And(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
Constraint::Lookup(
|
||||
symbol,
|
||||
Index::push_new(&mut self.expectations, expected),
|
||||
region,
|
||||
)
|
||||
}
|
||||
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
|
||||
match constraint {
|
||||
Constraint::Eq(..) => false,
|
||||
Constraint::Store(..) => false,
|
||||
Constraint::Lookup(..) => false,
|
||||
Constraint::Pattern(..) => false,
|
||||
Constraint::True => false,
|
||||
Constraint::SaveTheEnvironment => true,
|
||||
Constraint::Let(index) => {
|
||||
let let_constraint = &self.let_constraints[index.index()];
|
||||
|
||||
let offset = let_constraint.defs_and_ret_constraint.index();
|
||||
let defs_constraint = &self.constraints[offset];
|
||||
let ret_constraint = &self.constraints[offset + 1];
|
||||
|
||||
self.contains_save_the_environment(defs_constraint)
|
||||
|| self.contains_save_the_environment(ret_constraint)
|
||||
}
|
||||
Constraint::And(slice) => {
|
||||
let constraints = &self.constraints[slice.indices()];
|
||||
|
||||
constraints
|
||||
.iter()
|
||||
.any(|c| self.contains_save_the_environment(c))
|
||||
}
|
||||
Constraint::IsOpenType(_) => false,
|
||||
Constraint::IncludesTag(_) => false,
|
||||
Constraint::PatternPresence(_, _, _, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
variable: Variable,
|
||||
filename: &'static str,
|
||||
line_number: u32,
|
||||
) -> Constraint {
|
||||
let type_index = Index::push_new(&mut self.types, typ);
|
||||
let string_index = Index::push_new(&mut self.strings, filename);
|
||||
|
||||
Constraint::Store(type_index, variable, string_index, line_number)
|
||||
}
|
||||
}
|
||||
|
||||
static_assertions::assert_eq_size!([u8; 3 * 8], Constraint);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Constraint {
|
||||
Eq(Type, Expected<Type>, Category, Region),
|
||||
Store(Type, Variable, &'static str, u32),
|
||||
Lookup(Symbol, Expected<Type>, Region),
|
||||
Pattern(Region, PatternCategory, Type, PExpected<Type>),
|
||||
Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
|
||||
Store(Index<Type>, Variable, Index<&'static str>, u32),
|
||||
Lookup(Symbol, Index<Expected<Type>>, Region),
|
||||
Pattern(
|
||||
Index<Type>,
|
||||
Index<PExpected<Type>>,
|
||||
Index<PatternCategory>,
|
||||
Region,
|
||||
),
|
||||
True, // Used for things that always unify, e.g. blanks and runtime errors
|
||||
SaveTheEnvironment,
|
||||
Let(Box<LetConstraint>),
|
||||
And(Vec<Constraint>),
|
||||
Present(Type, PresenceConstraint),
|
||||
Let(Index<LetConstraint>),
|
||||
And(Slice<Constraint>),
|
||||
/// Presence constraints
|
||||
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
|
||||
IncludesTag(Index<IncludesTag>),
|
||||
PatternPresence(
|
||||
Index<Type>,
|
||||
Index<PExpected<Type>>,
|
||||
Index<PatternCategory>,
|
||||
Region,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LetConstraint {
|
||||
pub rigid_vars: Vec<Variable>,
|
||||
pub flex_vars: Vec<Variable>,
|
||||
pub def_types: SendMap<Symbol, Loc<Type>>,
|
||||
pub defs_constraint: Constraint,
|
||||
pub ret_constraint: Constraint,
|
||||
pub rigid_vars: Slice<Variable>,
|
||||
pub flex_vars: Slice<Variable>,
|
||||
pub def_types: Slice<(Symbol, Loc<Type>)>,
|
||||
pub defs_and_ret_constraint: Index<(Constraint, Constraint)>,
|
||||
}
|
||||
|
||||
// VALIDATE
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Declared {
|
||||
pub rigid_vars: MutSet<Variable>,
|
||||
pub flex_vars: MutSet<Variable>,
|
||||
}
|
||||
|
||||
impl Constraint {
|
||||
pub fn validate(&self) -> bool {
|
||||
let mut unbound = Default::default();
|
||||
|
||||
validate_help(self, &Declared::default(), &mut unbound);
|
||||
|
||||
if !unbound.type_variables.is_empty() {
|
||||
panic!("found unbound type variables {:?}", &unbound.type_variables);
|
||||
}
|
||||
|
||||
if !unbound.lambda_set_variables.is_empty() {
|
||||
panic!(
|
||||
"found unbound lambda set variables {:?}",
|
||||
&unbound.lambda_set_variables
|
||||
);
|
||||
}
|
||||
|
||||
if !unbound.recursion_variables.is_empty() {
|
||||
panic!(
|
||||
"found unbound recursion variables {:?}",
|
||||
&unbound.recursion_variables
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn contains_save_the_environment(&self) -> bool {
|
||||
match self {
|
||||
Constraint::Eq(_, _, _, _) => false,
|
||||
Constraint::Store(_, _, _, _) => false,
|
||||
Constraint::Lookup(_, _, _) => false,
|
||||
Constraint::Pattern(_, _, _, _) => false,
|
||||
Constraint::True => false,
|
||||
Constraint::SaveTheEnvironment => true,
|
||||
Constraint::Let(boxed) => {
|
||||
boxed.ret_constraint.contains_save_the_environment()
|
||||
|| boxed.defs_constraint.contains_save_the_environment()
|
||||
}
|
||||
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
||||
Constraint::Present(_, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDetail) {
|
||||
for var in &detail.type_variables {
|
||||
if !(declared.rigid_vars.contains(var) || declared.flex_vars.contains(var)) {
|
||||
accum.type_variables.insert(*var);
|
||||
}
|
||||
}
|
||||
|
||||
// lambda set variables are always flex
|
||||
for var in &detail.lambda_set_variables {
|
||||
if declared.rigid_vars.contains(var) {
|
||||
panic!("lambda set variable {:?} is declared as rigid", var);
|
||||
}
|
||||
|
||||
if !declared.flex_vars.contains(var) {
|
||||
accum.lambda_set_variables.push(*var);
|
||||
}
|
||||
}
|
||||
|
||||
// recursion vars should be always rigid
|
||||
for var in &detail.recursion_variables {
|
||||
if declared.flex_vars.contains(var) {
|
||||
panic!("recursion variable {:?} is declared as flex", var);
|
||||
}
|
||||
|
||||
if !declared.rigid_vars.contains(var) {
|
||||
accum.recursion_variables.insert(*var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_help(constraint: &Constraint, declared: &Declared, accum: &mut VariableDetail) {
|
||||
use Constraint::*;
|
||||
|
||||
match constraint {
|
||||
True | SaveTheEnvironment | Lookup(_, _, _) => { /* nothing */ }
|
||||
Store(typ, var, _, _) => {
|
||||
subtract(declared, &typ.variables_detail(), accum);
|
||||
|
||||
if !declared.flex_vars.contains(var) {
|
||||
accum.type_variables.insert(*var);
|
||||
}
|
||||
}
|
||||
Constraint::Eq(typ, expected, _, _) => {
|
||||
subtract(declared, &typ.variables_detail(), accum);
|
||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
||||
}
|
||||
Constraint::Pattern(_, _, typ, expected) => {
|
||||
subtract(declared, &typ.variables_detail(), accum);
|
||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
||||
}
|
||||
Constraint::Let(letcon) => {
|
||||
let mut declared = declared.clone();
|
||||
declared
|
||||
.rigid_vars
|
||||
.extend(letcon.rigid_vars.iter().copied());
|
||||
declared.flex_vars.extend(letcon.flex_vars.iter().copied());
|
||||
|
||||
validate_help(&letcon.defs_constraint, &declared, accum);
|
||||
validate_help(&letcon.ret_constraint, &declared, accum);
|
||||
}
|
||||
Constraint::And(inner) => {
|
||||
for c in inner {
|
||||
validate_help(c, declared, accum);
|
||||
}
|
||||
}
|
||||
Constraint::Present(typ, constr) => {
|
||||
subtract(declared, &typ.variables_detail(), accum);
|
||||
match constr {
|
||||
PresenceConstraint::IncludesTag(_, tys, _, _) => {
|
||||
for ty in tys {
|
||||
subtract(declared, &ty.variables_detail(), accum);
|
||||
}
|
||||
}
|
||||
PresenceConstraint::IsOpen => {}
|
||||
PresenceConstraint::Pattern(_, _, expected) => {
|
||||
subtract(declared, &typ.variables_detail(), accum);
|
||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct IncludesTag {
|
||||
pub type_index: Index<Type>,
|
||||
pub tag_name: TagName,
|
||||
pub types: Slice<Type>,
|
||||
pub pattern_category: Index<PatternCategory>,
|
||||
pub region: Region,
|
||||
}
|
||||
|
@ -161,13 +161,13 @@ where
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct Index(usize);
|
||||
pub struct HumanIndex(usize);
|
||||
|
||||
impl Index {
|
||||
pub const FIRST: Self = Index(0);
|
||||
impl HumanIndex {
|
||||
pub const FIRST: Self = HumanIndex(0);
|
||||
|
||||
pub fn zero_based(i: usize) -> Self {
|
||||
Index(i)
|
||||
HumanIndex(i)
|
||||
}
|
||||
|
||||
pub fn to_zero_based(self) -> usize {
|
||||
@ -175,7 +175,7 @@ impl Index {
|
||||
}
|
||||
|
||||
pub fn one_based(i: usize) -> Self {
|
||||
Index(i - 1)
|
||||
HumanIndex(i - 1)
|
||||
}
|
||||
|
||||
pub fn ordinal(self) -> std::string::String {
|
||||
|
@ -3,3 +3,4 @@
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod all;
|
||||
pub mod soa;
|
||||
|
96
compiler/collections/src/soa.rs
Normal file
96
compiler/collections/src/soa.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use std::usize;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Index<T> {
|
||||
index: u32,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Index<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Index({})", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<T> {
|
||||
pub const fn new(index: u32) -> Self {
|
||||
Self {
|
||||
index,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn index(&self) -> usize {
|
||||
self.index as usize
|
||||
}
|
||||
|
||||
pub fn push_new(vector: &mut Vec<T>, value: T) -> Index<T> {
|
||||
let index = Self::new(vector.len() as _);
|
||||
|
||||
vector.push(value);
|
||||
|
||||
index
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Slice<T> {
|
||||
start: u32,
|
||||
length: u16,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for Slice<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Slice(start = {}, length = {})", self.start, self.length)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Slice<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start: Default::default(),
|
||||
length: Default::default(),
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Slice<T> {
|
||||
pub const fn new(start: u32, length: u16) -> Self {
|
||||
Self {
|
||||
start,
|
||||
length,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extend_new<I>(vector: &mut Vec<T>, values: I) -> Slice<T>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
let start = vector.len() as u32;
|
||||
|
||||
vector.extend(values);
|
||||
|
||||
let end = vector.len() as u32;
|
||||
|
||||
Self::new(start, (end - start) as u16)
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> usize {
|
||||
self.length as _
|
||||
}
|
||||
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
|
||||
pub const fn indices(&self) -> std::ops::Range<usize> {
|
||||
self.start as usize..(self.start as usize + self.length as usize)
|
||||
}
|
||||
|
||||
pub fn into_iter(&self) -> impl Iterator<Item = Index<T>> {
|
||||
self.indices().map(|i| Index::new(i as _))
|
||||
}
|
||||
}
|
@ -14,3 +14,4 @@ roc_parse = { path = "../parse" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
arrayvec = "0.7.2"
|
||||
|
@ -1,8 +1,7 @@
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::LetConstraint;
|
||||
use arrayvec::ArrayVec;
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
@ -12,8 +11,10 @@ use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{AliasKind, Category};
|
||||
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn add_numeric_bound_constr(
|
||||
constrs: &mut Vec<Constraint>,
|
||||
constraints: &mut Constraints,
|
||||
num_constraints: &mut impl Extend<Constraint>,
|
||||
num_type: Type,
|
||||
bound: impl TypedNumericBound,
|
||||
region: Region,
|
||||
@ -27,12 +28,12 @@ pub fn add_numeric_bound_constr(
|
||||
0 => total_num_type,
|
||||
1 => {
|
||||
let actual_type = Variable(range[0]);
|
||||
constrs.push(Eq(
|
||||
total_num_type.clone(),
|
||||
Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region),
|
||||
category,
|
||||
region,
|
||||
));
|
||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||
let because_suffix =
|
||||
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
||||
|
||||
num_constraints.extend([because_suffix]);
|
||||
|
||||
total_num_type
|
||||
}
|
||||
_ => RangedNumber(Box::new(total_num_type), range),
|
||||
@ -41,6 +42,7 @@ pub fn add_numeric_bound_constr(
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
@ -49,31 +51,35 @@ pub fn int_literal(
|
||||
) -> Constraint {
|
||||
let reason = Reason::IntLiteral;
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
||||
// like "U8".
|
||||
// Always add the bound first; this improves the resolved type quality in case it's an alias like "U8".
|
||||
let mut constrs = ArrayVec::<_, 3>::new();
|
||||
let num_type = add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
constrs.extend(vec![
|
||||
Eq(
|
||||
|
||||
constrs.extend([
|
||||
constraints.equal_types(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Int, region),
|
||||
constraints.equal_types(num_type, expected, Category::Int, region),
|
||||
]);
|
||||
|
||||
exists(vec![num_var], And(constrs))
|
||||
// TODO the precision_var is not part of the exists here; for float it is. Which is correct?
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
@ -82,29 +88,33 @@ pub fn float_literal(
|
||||
) -> Constraint {
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
let mut constrs = ArrayVec::<_, 3>::new();
|
||||
let num_type = add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
constrs.extend(vec![
|
||||
Eq(
|
||||
|
||||
constrs.extend([
|
||||
constraints.equal_types(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
constraints.equal_types(num_type, expected, Category::Float, region),
|
||||
]);
|
||||
|
||||
exists(vec![num_var, precision_var], And(constrs))
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var, precision_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_literal(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
@ -112,23 +122,20 @@ pub fn num_literal(
|
||||
) -> Constraint {
|
||||
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
let num_type =
|
||||
add_numeric_bound_constr(&mut constrs, open_number_type, bound, region, Category::Num);
|
||||
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]);
|
||||
let mut constrs = ArrayVec::<_, 2>::new();
|
||||
let num_type = add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
open_number_type,
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
exists(vec![num_var], And(constrs))
|
||||
}
|
||||
constrs.extend([constraints.equal_types(num_type, expected, Category::Num, region)]);
|
||||
|
||||
#[inline(always)]
|
||||
pub fn exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint {
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars,
|
||||
def_types: SendMap::default(),
|
||||
defs_constraint: constraint,
|
||||
ret_constraint: Constraint::True,
|
||||
}))
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
use crate::expr::constrain_decls;
|
||||
use roc_builtins::std::StdLib;
|
||||
use roc_can::constraint::{Constraint, LetConstraint};
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::def::Declaration;
|
||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
@ -17,13 +16,12 @@ pub enum ExposedModuleTypes {
|
||||
Valid(MutMap<Symbol, SolvedType>, MutMap<Symbol, Alias>),
|
||||
}
|
||||
|
||||
pub struct ConstrainedModule {
|
||||
pub unused_imports: MutMap<ModuleId, Region>,
|
||||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
pub fn constrain_module(declarations: &[Declaration], home: ModuleId) -> Constraint {
|
||||
constrain_decls(home, declarations)
|
||||
pub fn constrain_module(
|
||||
constraints: &mut Constraints,
|
||||
declarations: &[Declaration],
|
||||
home: ModuleId,
|
||||
) -> Constraint {
|
||||
crate::expr::constrain_decls(constraints, home, declarations)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -33,11 +31,11 @@ pub struct Import {
|
||||
}
|
||||
|
||||
pub fn constrain_imported_values(
|
||||
constraints: &mut Constraints,
|
||||
imports: Vec<Import>,
|
||||
body_con: Constraint,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Vec<Variable>, Constraint) {
|
||||
use Constraint::*;
|
||||
let mut def_types = SendMap::default();
|
||||
let mut rigid_vars = Vec::new();
|
||||
|
||||
@ -84,24 +82,19 @@ pub fn constrain_imported_values(
|
||||
|
||||
(
|
||||
rigid_vars.clone(),
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars,
|
||||
flex_vars: Vec::new(),
|
||||
def_types,
|
||||
defs_constraint: True,
|
||||
ret_constraint: body_con,
|
||||
})),
|
||||
constraints.let_constraint(rigid_vars, [], def_types, Constraint::True, body_con),
|
||||
)
|
||||
}
|
||||
|
||||
/// Run pre_constrain_imports to get imported_symbols and imported_aliases.
|
||||
pub fn constrain_imports(
|
||||
constraints: &mut Constraints,
|
||||
imported_symbols: Vec<Import>,
|
||||
constraint: Constraint,
|
||||
var_store: &mut VarStore,
|
||||
) -> Constraint {
|
||||
let (_introduced_rigids, constraint) =
|
||||
constrain_imported_values(imported_symbols, constraint, var_store);
|
||||
constrain_imported_values(constraints, imported_symbols, constraint, var_store);
|
||||
|
||||
// TODO determine what to do with those rigids
|
||||
// for var in introduced_rigids {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::builtins;
|
||||
use crate::expr::{constrain_expr, Env};
|
||||
use roc_can::constraint::{Constraint, PresenceConstraint};
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::pattern::Pattern::{self, *};
|
||||
use roc_can::pattern::{DestructType, RecordDestruct};
|
||||
use roc_collections::all::{Index, SendMap};
|
||||
use roc_collections::all::{HumanIndex, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
@ -155,6 +155,7 @@ fn headers_from_annotation_help(
|
||||
/// initialize the Vecs in PatternState using with_capacity
|
||||
/// based on its knowledge of their lengths.
|
||||
pub fn constrain_pattern(
|
||||
constraints: &mut Constraints,
|
||||
env: &Env,
|
||||
pattern: &Pattern,
|
||||
region: Region,
|
||||
@ -169,20 +170,18 @@ pub fn constrain_pattern(
|
||||
// A -> ""
|
||||
// _ -> ""
|
||||
// so, we know that "x" (in this case, a tag union) must be open.
|
||||
state.constraints.push(Constraint::Present(
|
||||
expected.get_type(),
|
||||
PresenceConstraint::IsOpen,
|
||||
));
|
||||
state
|
||||
.constraints
|
||||
.push(constraints.is_open_type(expected.get_type()));
|
||||
}
|
||||
UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => {
|
||||
// Erroneous patterns don't add any constraints.
|
||||
}
|
||||
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
expected.get_type_ref().clone(),
|
||||
PresenceConstraint::IsOpen,
|
||||
));
|
||||
state
|
||||
.constraints
|
||||
.push(constraints.is_open_type(expected.get_type_ref().clone()));
|
||||
state.headers.insert(
|
||||
*symbol,
|
||||
Loc {
|
||||
@ -198,6 +197,7 @@ pub fn constrain_pattern(
|
||||
let num_type = builtins::num_num(Type::Variable(var));
|
||||
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
num_type,
|
||||
bound,
|
||||
@ -205,11 +205,11 @@ pub fn constrain_pattern(
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Num,
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
num_type,
|
||||
expected,
|
||||
PatternCategory::Num,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
@ -217,6 +217,7 @@ pub fn constrain_pattern(
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
@ -227,7 +228,7 @@ pub fn constrain_pattern(
|
||||
// Link the free num var with the int var and our expectation.
|
||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
state.constraints.push(constraints.equal_types(
|
||||
num_type, // TODO check me if something breaks!
|
||||
Expected::NoExpectation(int_type),
|
||||
Category::Int,
|
||||
@ -235,11 +236,11 @@ pub fn constrain_pattern(
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Int,
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
Type::Variable(num_var),
|
||||
expected,
|
||||
PatternCategory::Int,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
@ -247,6 +248,7 @@ pub fn constrain_pattern(
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
@ -257,7 +259,7 @@ pub fn constrain_pattern(
|
||||
// Link the free num var with the float var and our expectation.
|
||||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
state.constraints.push(constraints.equal_types(
|
||||
num_type.clone(), // TODO check me if something breaks!
|
||||
Expected::NoExpectation(float_type),
|
||||
Category::Float,
|
||||
@ -265,29 +267,29 @@ pub fn constrain_pattern(
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
num_type, // TODO check me if something breaks!
|
||||
expected,
|
||||
PatternCategory::Float,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
StrLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Str,
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
builtins::str_type(),
|
||||
expected,
|
||||
PatternCategory::Str,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
SingleQuote(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Character,
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
builtins::num_u32(),
|
||||
expected,
|
||||
PatternCategory::Character,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
@ -324,36 +326,39 @@ pub fn constrain_pattern(
|
||||
|
||||
let field_type = match typ {
|
||||
DestructType::Guard(guard_var, loc_guard) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
state.constraints.push(constraints.pattern_presence(
|
||||
Type::Variable(*guard_var),
|
||||
PresenceConstraint::Pattern(
|
||||
region,
|
||||
PatternCategory::PatternGuard,
|
||||
PExpected::ForReason(
|
||||
PReason::PatternGuard,
|
||||
pat_type.clone(),
|
||||
loc_guard.region,
|
||||
),
|
||||
PExpected::ForReason(
|
||||
PReason::PatternGuard,
|
||||
pat_type.clone(),
|
||||
loc_guard.region,
|
||||
),
|
||||
PatternCategory::PatternGuard,
|
||||
region,
|
||||
));
|
||||
state.vars.push(*guard_var);
|
||||
|
||||
constrain_pattern(env, &loc_guard.value, loc_guard.region, expected, state);
|
||||
constrain_pattern(
|
||||
constraints,
|
||||
env,
|
||||
&loc_guard.value,
|
||||
loc_guard.region,
|
||||
expected,
|
||||
state,
|
||||
);
|
||||
|
||||
RecordField::Demanded(pat_type)
|
||||
}
|
||||
DestructType::Optional(expr_var, loc_expr) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
state.constraints.push(constraints.pattern_presence(
|
||||
Type::Variable(*expr_var),
|
||||
PresenceConstraint::Pattern(
|
||||
region,
|
||||
PatternCategory::PatternDefault,
|
||||
PExpected::ForReason(
|
||||
PReason::OptionalField,
|
||||
pat_type.clone(),
|
||||
loc_expr.region,
|
||||
),
|
||||
PExpected::ForReason(
|
||||
PReason::OptionalField,
|
||||
pat_type.clone(),
|
||||
loc_expr.region,
|
||||
),
|
||||
PatternCategory::PatternDefault,
|
||||
region,
|
||||
));
|
||||
|
||||
state.vars.push(*expr_var);
|
||||
@ -364,8 +369,13 @@ pub fn constrain_pattern(
|
||||
loc_expr.region,
|
||||
);
|
||||
|
||||
let expr_con =
|
||||
constrain_expr(env, loc_expr.region, &loc_expr.value, expr_expected);
|
||||
let expr_con = constrain_expr(
|
||||
constraints,
|
||||
env,
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
expr_expected,
|
||||
);
|
||||
state.constraints.push(expr_con);
|
||||
|
||||
RecordField::Optional(pat_type)
|
||||
@ -383,16 +393,18 @@ pub fn constrain_pattern(
|
||||
|
||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||
|
||||
let whole_con = Constraint::Eq(
|
||||
let whole_con = constraints.equal_types(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(record_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
let record_con = Constraint::Present(
|
||||
let record_con = constraints.pattern_presence(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, PatternCategory::Record, expected),
|
||||
expected,
|
||||
PatternCategory::Record,
|
||||
region,
|
||||
);
|
||||
|
||||
state.constraints.push(whole_con);
|
||||
@ -414,29 +426,36 @@ pub fn constrain_pattern(
|
||||
let expected = PExpected::ForReason(
|
||||
PReason::TagArg {
|
||||
tag_name: tag_name.clone(),
|
||||
index: Index::zero_based(index),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
pattern_type,
|
||||
region,
|
||||
);
|
||||
constrain_pattern(env, &loc_pattern.value, loc_pattern.region, expected, state);
|
||||
constrain_pattern(
|
||||
constraints,
|
||||
env,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
expected,
|
||||
state,
|
||||
);
|
||||
}
|
||||
|
||||
let pat_category = PatternCategory::Ctor(tag_name.clone());
|
||||
|
||||
let whole_con = Constraint::Present(
|
||||
let whole_con = constraints.includes_tag(
|
||||
expected.clone().get_type(),
|
||||
PresenceConstraint::IncludesTag(
|
||||
tag_name.clone(),
|
||||
argument_types.clone(),
|
||||
region,
|
||||
pat_category.clone(),
|
||||
),
|
||||
tag_name.clone(),
|
||||
argument_types.clone(),
|
||||
pat_category.clone(),
|
||||
region,
|
||||
);
|
||||
|
||||
let tag_con = Constraint::Present(
|
||||
let tag_con = constraints.pattern_presence(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, pat_category, expected),
|
||||
expected,
|
||||
pat_category,
|
||||
region,
|
||||
);
|
||||
|
||||
state.vars.push(*whole_var);
|
||||
@ -468,6 +487,7 @@ pub fn constrain_pattern(
|
||||
// First, add a constraint for the argument "who"
|
||||
let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone());
|
||||
constrain_pattern(
|
||||
constraints,
|
||||
env,
|
||||
&loc_arg_pattern.value,
|
||||
loc_arg_pattern.region,
|
||||
@ -476,7 +496,7 @@ pub fn constrain_pattern(
|
||||
);
|
||||
|
||||
// Next, link `whole_var` to the opaque type of "@Id who"
|
||||
let whole_con = Constraint::Eq(
|
||||
let whole_con = constraints.equal_types(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(opaque_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
@ -486,7 +506,7 @@ pub fn constrain_pattern(
|
||||
// Link the entire wrapped opaque type (with the now-constrained argument) to the type
|
||||
// variables of the opaque type
|
||||
// TODO: better expectation here
|
||||
let link_type_variables_con = Constraint::Eq(
|
||||
let link_type_variables_con = constraints.equal_types(
|
||||
(**specialized_def_type).clone(),
|
||||
Expected::NoExpectation(arg_pattern_type),
|
||||
Category::OpaqueWrap(*opaque),
|
||||
@ -494,9 +514,11 @@ pub fn constrain_pattern(
|
||||
);
|
||||
|
||||
// Next, link `whole_var` (the type of "@Id who") to the expected type
|
||||
let opaque_pattern_con = Constraint::Present(
|
||||
let opaque_pattern_con = constraints.pattern_presence(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, PatternCategory::Opaque(*opaque), expected),
|
||||
expected,
|
||||
PatternCategory::Opaque(*opaque),
|
||||
region,
|
||||
);
|
||||
|
||||
state
|
||||
|
@ -1,4 +1,4 @@
|
||||
use roc_collections::all::{Index, MutMap};
|
||||
use roc_collections::all::{HumanIndex, MutMap};
|
||||
use roc_module::ident::{Lowercase, TagIdIntType, TagName};
|
||||
use roc_region::all::Region;
|
||||
use roc_std::RocDec;
|
||||
@ -70,7 +70,7 @@ pub enum Error {
|
||||
Redundant {
|
||||
overall_region: Region,
|
||||
branch_region: Region,
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -293,6 +293,9 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||
|
||||
SpaceBefore(ann, spaces) => {
|
||||
buf.newline();
|
||||
|
||||
buf.indent(indent);
|
||||
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
ann.format_with_options(buf, parens, Newlines::No, indent)
|
||||
}
|
||||
|
@ -2984,6 +2984,18 @@ mod test_fmt {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_higher_order_function() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
foo :
|
||||
(Str -> Bool) -> Bool
|
||||
|
||||
42
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test that everything under examples/ is formatted correctly
|
||||
/// If this test fails on your diff, it probably means you need to re-format the examples.
|
||||
|
@ -5,14 +5,14 @@ use crossbeam::deque::{Injector, Stealer, Worker};
|
||||
use crossbeam::thread;
|
||||
use parking_lot::Mutex;
|
||||
use roc_builtins::std::StdLib;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_can::def::Declaration;
|
||||
use roc_can::module::{canonicalize_module_defs, Module};
|
||||
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
|
||||
use roc_constrain::module::{
|
||||
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
||||
constrain_imports, constrain_module, pre_constrain_imports, ConstrainableImports,
|
||||
ExposedModuleTypes, Import, SubsByModule,
|
||||
};
|
||||
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||
use roc_module::symbol::{
|
||||
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified,
|
||||
@ -229,6 +229,7 @@ fn start_phase<'a>(
|
||||
module,
|
||||
ident_ids,
|
||||
module_timing,
|
||||
constraints,
|
||||
constraint,
|
||||
var_store,
|
||||
imported_modules,
|
||||
@ -241,6 +242,7 @@ fn start_phase<'a>(
|
||||
module,
|
||||
ident_ids,
|
||||
module_timing,
|
||||
constraints,
|
||||
constraint,
|
||||
var_store,
|
||||
imported_modules,
|
||||
@ -391,7 +393,8 @@ struct ConstrainedModule {
|
||||
module: Module,
|
||||
declarations: Vec<Declaration>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
constraint: Constraint,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
ident_ids: IdentIds,
|
||||
var_store: VarStore,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
@ -728,7 +731,8 @@ enum BuildTask<'a> {
|
||||
ident_ids: IdentIds,
|
||||
imported_symbols: Vec<Import>,
|
||||
module_timing: ModuleTiming,
|
||||
constraint: Constraint,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
var_store: VarStore,
|
||||
declarations: Vec<Declaration>,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
@ -3027,7 +3031,8 @@ impl<'a> BuildTask<'a> {
|
||||
module: Module,
|
||||
ident_ids: IdentIds,
|
||||
module_timing: ModuleTiming,
|
||||
constraint: Constraint,
|
||||
constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
var_store: VarStore,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
exposed_types: &mut SubsByModule,
|
||||
@ -3057,6 +3062,7 @@ impl<'a> BuildTask<'a> {
|
||||
module,
|
||||
ident_ids,
|
||||
imported_symbols,
|
||||
constraints,
|
||||
constraint,
|
||||
var_store,
|
||||
declarations,
|
||||
@ -3073,7 +3079,8 @@ fn run_solve<'a>(
|
||||
ident_ids: IdentIds,
|
||||
mut module_timing: ModuleTiming,
|
||||
imported_symbols: Vec<Import>,
|
||||
constraint: Constraint,
|
||||
mut constraints: Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
mut var_store: VarStore,
|
||||
decls: Vec<Declaration>,
|
||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||
@ -3084,7 +3091,12 @@ fn run_solve<'a>(
|
||||
|
||||
// Finish constraining the module by wrapping the existing Constraint
|
||||
// in the ones we just computed. We can do this off the main thread.
|
||||
let constraint = constrain_imports(imported_symbols, constraint, &mut var_store);
|
||||
let constraint = constrain_imports(
|
||||
&mut constraints,
|
||||
imported_symbols,
|
||||
constraint,
|
||||
&mut var_store,
|
||||
);
|
||||
|
||||
let constrain_end = SystemTime::now();
|
||||
|
||||
@ -3097,12 +3109,11 @@ fn run_solve<'a>(
|
||||
..
|
||||
} = module;
|
||||
|
||||
if false {
|
||||
debug_assert!(constraint.validate(), "{:?}", &constraint);
|
||||
}
|
||||
// TODO
|
||||
// if false { debug_assert!(constraint.validate(), "{:?}", &constraint); }
|
||||
|
||||
let (solved_subs, solved_env, problems) =
|
||||
roc_solve::module::run_solve(rigid_variables, constraint, var_store);
|
||||
roc_solve::module::run_solve(&constraints, constraint, rigid_variables, var_store);
|
||||
|
||||
let exposed_vars_by_symbol: Vec<_> = solved_env
|
||||
.vars_by_symbol()
|
||||
@ -3247,7 +3258,9 @@ fn canonicalize_and_constrain<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let constraint = constrain_module(&module_output.declarations, module_id);
|
||||
let mut constraints = Constraints::new();
|
||||
let constraint =
|
||||
constrain_module(&mut constraints, &module_output.declarations, module_id);
|
||||
|
||||
let module = Module {
|
||||
module_id,
|
||||
@ -3263,6 +3276,7 @@ fn canonicalize_and_constrain<'a>(
|
||||
declarations: module_output.declarations,
|
||||
imported_modules,
|
||||
var_store,
|
||||
constraints,
|
||||
constraint,
|
||||
ident_ids: module_output.ident_ids,
|
||||
dep_idents,
|
||||
@ -3745,6 +3759,7 @@ fn run_task<'a>(
|
||||
module,
|
||||
module_timing,
|
||||
imported_symbols,
|
||||
constraints,
|
||||
constraint,
|
||||
var_store,
|
||||
ident_ids,
|
||||
@ -3756,6 +3771,7 @@ fn run_task<'a>(
|
||||
ident_ids,
|
||||
module_timing,
|
||||
imported_symbols,
|
||||
constraints,
|
||||
constraint,
|
||||
var_store,
|
||||
declarations,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ir::DestructType;
|
||||
use roc_collections::all::Index;
|
||||
use roc_collections::all::HumanIndex;
|
||||
use roc_exhaustive::{
|
||||
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
||||
};
|
||||
@ -189,7 +189,7 @@ fn to_nonredundant_rows(
|
||||
return Err(Error::Redundant {
|
||||
overall_region,
|
||||
branch_region: region,
|
||||
index: Index::zero_based(checked_rows.len()),
|
||||
index: HumanIndex::zero_based(checked_rows.len()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::solve;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -17,8 +17,9 @@ pub struct SolvedModule {
|
||||
}
|
||||
|
||||
pub fn run_solve(
|
||||
constraints: &Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
rigid_variables: MutMap<Variable, Lowercase>,
|
||||
constraint: Constraint,
|
||||
var_store: VarStore,
|
||||
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
||||
let env = solve::Env::default();
|
||||
@ -34,7 +35,7 @@ pub fn run_solve(
|
||||
let mut problems = Vec::new();
|
||||
|
||||
// Run the solver to populate Subs.
|
||||
let (solved_subs, solved_env) = solve::run(&env, &mut problems, subs, &constraint);
|
||||
let (solved_subs, solved_env) = solve::run(constraints, &env, &mut problems, subs, &constraint);
|
||||
|
||||
(solved_subs, solved_env, problems)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use bumpalo::Bump;
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::PresenceConstraint;
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::ident::TagName;
|
||||
@ -170,18 +169,20 @@ struct State {
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
constraints: &Constraints,
|
||||
env: &Env,
|
||||
problems: &mut Vec<TypeError>,
|
||||
mut subs: Subs,
|
||||
constraint: &Constraint,
|
||||
) -> (Solved<Subs>, Env) {
|
||||
let env = run_in_place(env, problems, &mut subs, constraint);
|
||||
let env = run_in_place(constraints, env, problems, &mut subs, constraint);
|
||||
|
||||
(Solved(subs), env)
|
||||
}
|
||||
|
||||
/// Modify an existing subs in-place instead
|
||||
pub fn run_in_place(
|
||||
constraints: &Constraints,
|
||||
env: &Env,
|
||||
problems: &mut Vec<TypeError>,
|
||||
subs: &mut Subs,
|
||||
@ -198,6 +199,7 @@ pub fn run_in_place(
|
||||
|
||||
let state = solve(
|
||||
&arena,
|
||||
constraints,
|
||||
env,
|
||||
state,
|
||||
rank,
|
||||
@ -211,24 +213,19 @@ pub fn run_in_place(
|
||||
state.env
|
||||
}
|
||||
|
||||
enum After {
|
||||
CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc<Variable>)>),
|
||||
}
|
||||
|
||||
enum Work<'a> {
|
||||
enum SolveWork<'a> {
|
||||
Constraint {
|
||||
env: &'a Env,
|
||||
rank: Rank,
|
||||
constraint: &'a Constraint,
|
||||
after: Option<After>,
|
||||
},
|
||||
/// Something to be done after a constraint and all its dependencies are fully solved.
|
||||
After(After),
|
||||
CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc<Variable>)>),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn solve(
|
||||
arena: &Bump,
|
||||
constraints: &Constraints,
|
||||
env: &Env,
|
||||
mut state: State,
|
||||
rank: Rank,
|
||||
@ -238,35 +235,29 @@ fn solve(
|
||||
subs: &mut Subs,
|
||||
constraint: &Constraint,
|
||||
) -> State {
|
||||
let mut stack = vec![Work::Constraint {
|
||||
use Constraint::*;
|
||||
|
||||
let initial = SolveWork::Constraint {
|
||||
env,
|
||||
rank,
|
||||
constraint,
|
||||
after: None,
|
||||
}];
|
||||
};
|
||||
let mut stack = vec![initial];
|
||||
|
||||
while let Some(work_item) = stack.pop() {
|
||||
let (env, rank, constraint) = match work_item {
|
||||
Work::After(After::CheckForInfiniteTypes(def_vars)) => {
|
||||
SolveWork::CheckForInfiniteTypes(def_vars) => {
|
||||
for (symbol, loc_var) in def_vars.iter() {
|
||||
check_for_infinite_type(subs, problems, *symbol, *loc_var);
|
||||
}
|
||||
// No constraint to be solved
|
||||
continue;
|
||||
}
|
||||
Work::Constraint {
|
||||
SolveWork::Constraint {
|
||||
env,
|
||||
rank,
|
||||
constraint,
|
||||
after,
|
||||
} => {
|
||||
// Push the `after` on first so that we look at it immediately after finishing all
|
||||
// the children of this constraint.
|
||||
if let Some(after) = after {
|
||||
stack.push(Work::After(after));
|
||||
}
|
||||
(env, rank, constraint)
|
||||
}
|
||||
} => (env, rank, constraint),
|
||||
};
|
||||
|
||||
state = match constraint {
|
||||
@ -279,7 +270,11 @@ fn solve(
|
||||
|
||||
copy
|
||||
}
|
||||
Eq(typ, expectation, category, region) => {
|
||||
Eq(type_index, expectation_index, category_index, region) => {
|
||||
let typ = &constraints.types[type_index.index()];
|
||||
let expectation = &constraints.expectations[expectation_index.index()];
|
||||
let category = &constraints.categories[category_index.index()];
|
||||
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
let expected = type_to_var(
|
||||
subs,
|
||||
@ -318,7 +313,9 @@ fn solve(
|
||||
}
|
||||
}
|
||||
}
|
||||
Store(source, target, _filename, _linenr) => {
|
||||
Store(source_index, target, _filename, _linenr) => {
|
||||
let source = &constraints.types[source_index.index()];
|
||||
|
||||
// a special version of Eq that is used to store types in the AST.
|
||||
// IT DOES NOT REPORT ERRORS!
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, source);
|
||||
@ -346,7 +343,7 @@ fn solve(
|
||||
}
|
||||
}
|
||||
}
|
||||
Lookup(symbol, expectation, region) => {
|
||||
Lookup(symbol, expectation_index, region) => {
|
||||
match env.get_var_by_symbol(symbol) {
|
||||
Some(var) => {
|
||||
// Deep copy the vars associated with this symbol before unifying them.
|
||||
@ -371,6 +368,8 @@ fn solve(
|
||||
// is being looked up in this module, then we use our Subs as both
|
||||
// the source and destination.
|
||||
let actual = deep_copy_var(subs, rank, pools, var);
|
||||
|
||||
let expectation = &constraints.expectations[expectation_index.index()];
|
||||
let expected = type_to_var(
|
||||
subs,
|
||||
rank,
|
||||
@ -378,6 +377,7 @@ fn solve(
|
||||
cached_aliases,
|
||||
expectation.get_type_ref(),
|
||||
);
|
||||
|
||||
match unify(subs, actual, expected, Mode::EQ) {
|
||||
Success(vars) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
@ -415,20 +415,24 @@ fn solve(
|
||||
}
|
||||
}
|
||||
}
|
||||
And(sub_constraints) => {
|
||||
for sub_constraint in sub_constraints.iter().rev() {
|
||||
stack.push(Work::Constraint {
|
||||
And(slice) => {
|
||||
let it = constraints.constraints[slice.indices()].iter().rev();
|
||||
for sub_constraint in it {
|
||||
stack.push(SolveWork::Constraint {
|
||||
env,
|
||||
rank,
|
||||
constraint: sub_constraint,
|
||||
after: None,
|
||||
})
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
Pattern(region, category, typ, expectation)
|
||||
| Present(typ, PresenceConstraint::Pattern(region, category, expectation)) => {
|
||||
Pattern(type_index, expectation_index, category_index, region)
|
||||
| PatternPresence(type_index, expectation_index, category_index, region) => {
|
||||
let typ = &constraints.types[type_index.index()];
|
||||
let expectation = &constraints.pattern_expectations[expectation_index.index()];
|
||||
let category = &constraints.pattern_categories[category_index.index()];
|
||||
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
let expected = type_to_var(
|
||||
subs,
|
||||
@ -439,7 +443,7 @@ fn solve(
|
||||
);
|
||||
|
||||
let mode = match constraint {
|
||||
Present(_, _) => Mode::PRESENT,
|
||||
PatternPresence(..) => Mode::PRESENT,
|
||||
_ => Mode::EQ,
|
||||
};
|
||||
|
||||
@ -472,18 +476,28 @@ fn solve(
|
||||
}
|
||||
}
|
||||
}
|
||||
Let(let_con) => {
|
||||
match &let_con.ret_constraint {
|
||||
Let(index) => {
|
||||
let let_con = &constraints.let_constraints[index.index()];
|
||||
|
||||
let offset = let_con.defs_and_ret_constraint.index();
|
||||
let defs_constraint = &constraints.constraints[offset];
|
||||
let ret_constraint = &constraints.constraints[offset + 1];
|
||||
|
||||
let flex_vars = &constraints.variables[let_con.flex_vars.indices()];
|
||||
let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()];
|
||||
|
||||
let def_types = &constraints.def_types[let_con.def_types.indices()];
|
||||
|
||||
match &ret_constraint {
|
||||
True if let_con.rigid_vars.is_empty() => {
|
||||
introduce(subs, rank, pools, &let_con.flex_vars);
|
||||
introduce(subs, rank, pools, flex_vars);
|
||||
|
||||
// If the return expression is guaranteed to solve,
|
||||
// solve the assignments themselves and move on.
|
||||
stack.push(Work::Constraint {
|
||||
stack.push(SolveWork::Constraint {
|
||||
env,
|
||||
rank,
|
||||
constraint: &let_con.defs_constraint,
|
||||
after: None,
|
||||
constraint: defs_constraint,
|
||||
});
|
||||
state
|
||||
}
|
||||
@ -491,6 +505,7 @@ fn solve(
|
||||
// TODO: make into `WorkItem` with `After`
|
||||
let state = solve(
|
||||
arena,
|
||||
constraints,
|
||||
env,
|
||||
state,
|
||||
rank,
|
||||
@ -498,22 +513,22 @@ fn solve(
|
||||
problems,
|
||||
cached_aliases,
|
||||
subs,
|
||||
&let_con.defs_constraint,
|
||||
defs_constraint,
|
||||
);
|
||||
|
||||
// Add a variable for each def to new_vars_by_env.
|
||||
let mut local_def_vars =
|
||||
LocalDefVarsVec::with_length(let_con.def_types.len());
|
||||
|
||||
for (symbol, loc_type) in let_con.def_types.iter() {
|
||||
let var =
|
||||
type_to_var(subs, rank, pools, cached_aliases, &loc_type.value);
|
||||
for (symbol, loc_type_index) in def_types.iter() {
|
||||
let typ = &constraints.types[loc_type_index.value.index()];
|
||||
let var = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
|
||||
local_def_vars.push((
|
||||
*symbol,
|
||||
Loc {
|
||||
value: var,
|
||||
region: loc_type.region,
|
||||
region: loc_type_index.region,
|
||||
},
|
||||
));
|
||||
}
|
||||
@ -523,19 +538,16 @@ fn solve(
|
||||
new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
||||
}
|
||||
|
||||
stack.push(Work::Constraint {
|
||||
stack.push(SolveWork::CheckForInfiniteTypes(local_def_vars));
|
||||
stack.push(SolveWork::Constraint {
|
||||
env: arena.alloc(new_env),
|
||||
rank,
|
||||
constraint: ret_con,
|
||||
after: Some(After::CheckForInfiniteTypes(local_def_vars)),
|
||||
});
|
||||
|
||||
state
|
||||
}
|
||||
ret_con => {
|
||||
let rigid_vars = &let_con.rigid_vars;
|
||||
let flex_vars = &let_con.flex_vars;
|
||||
|
||||
// work in the next pool to localize header
|
||||
let next_rank = rank.next();
|
||||
|
||||
@ -568,8 +580,8 @@ fn solve(
|
||||
let mut local_def_vars =
|
||||
LocalDefVarsVec::with_length(let_con.def_types.len());
|
||||
|
||||
for (symbol, loc_type) in let_con.def_types.iter() {
|
||||
let def_type = &loc_type.value;
|
||||
for (symbol, loc_type) in def_types.iter() {
|
||||
let def_type = &constraints.types[loc_type.value.index()];
|
||||
|
||||
let var = type_to_var(subs, next_rank, pools, cached_aliases, def_type);
|
||||
|
||||
@ -589,6 +601,7 @@ fn solve(
|
||||
mark,
|
||||
} = solve(
|
||||
arena,
|
||||
constraints,
|
||||
env,
|
||||
state,
|
||||
next_rank,
|
||||
@ -596,7 +609,7 @@ fn solve(
|
||||
problems,
|
||||
cached_aliases,
|
||||
subs,
|
||||
&let_con.defs_constraint,
|
||||
defs_constraint,
|
||||
);
|
||||
|
||||
let young_mark = mark;
|
||||
@ -667,18 +680,20 @@ fn solve(
|
||||
|
||||
// Now solve the body, using the new vars_by_symbol which includes
|
||||
// the assignments' name-to-variable mappings.
|
||||
stack.push(Work::Constraint {
|
||||
stack.push(SolveWork::CheckForInfiniteTypes(local_def_vars));
|
||||
stack.push(SolveWork::Constraint {
|
||||
env: arena.alloc(new_env),
|
||||
rank,
|
||||
constraint: ret_con,
|
||||
after: Some(After::CheckForInfiniteTypes(local_def_vars)),
|
||||
});
|
||||
|
||||
state_for_ret_con
|
||||
}
|
||||
}
|
||||
}
|
||||
Present(typ, PresenceConstraint::IsOpen) => {
|
||||
IsOpenType(type_index) => {
|
||||
let typ = &constraints.types[type_index.index()];
|
||||
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
let mut new_desc = subs.get(actual);
|
||||
match new_desc.content {
|
||||
@ -700,13 +715,24 @@ fn solve(
|
||||
}
|
||||
}
|
||||
}
|
||||
Present(
|
||||
typ,
|
||||
PresenceConstraint::IncludesTag(tag_name, tys, region, pattern_category),
|
||||
) => {
|
||||
IncludesTag(index) => {
|
||||
let includes_tag = &constraints.includes_tags[index.index()];
|
||||
|
||||
let roc_can::constraint::IncludesTag {
|
||||
type_index,
|
||||
tag_name,
|
||||
types,
|
||||
pattern_category,
|
||||
region,
|
||||
} = includes_tag;
|
||||
|
||||
let typ = &constraints.types[type_index.index()];
|
||||
let tys = &constraints.types[types.indices()];
|
||||
let pattern_category = &constraints.pattern_categories[pattern_category.index()];
|
||||
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
let tag_ty = Type::TagUnion(
|
||||
vec![(tag_name.clone(), tys.clone())],
|
||||
vec![(tag_name.clone(), tys.to_vec())],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
);
|
||||
let includes = type_to_var(subs, rank, pools, cached_aliases, &tag_ty);
|
||||
|
@ -2,7 +2,7 @@ use crate::pretty_print::Parens;
|
||||
use crate::subs::{
|
||||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
||||
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutSet, SendMap};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||
@ -1203,14 +1203,14 @@ pub struct TagUnionStructure<'a> {
|
||||
pub enum PReason {
|
||||
TypedArg {
|
||||
opt_name: Option<Symbol>,
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
WhenMatch {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
TagArg {
|
||||
tag_name: TagName,
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
PatternGuard,
|
||||
OptionalField,
|
||||
@ -1219,12 +1219,12 @@ pub enum PReason {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AnnotationSource {
|
||||
TypedIfBranch {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
num_branches: usize,
|
||||
region: Region,
|
||||
},
|
||||
TypedWhenBranch {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
region: Region,
|
||||
},
|
||||
TypedBody {
|
||||
@ -1246,7 +1246,7 @@ impl AnnotationSource {
|
||||
pub enum Reason {
|
||||
FnArg {
|
||||
name: Option<Symbol>,
|
||||
arg_index: Index,
|
||||
arg_index: HumanIndex,
|
||||
},
|
||||
FnCall {
|
||||
name: Option<Symbol>,
|
||||
@ -1254,28 +1254,28 @@ pub enum Reason {
|
||||
},
|
||||
LowLevelOpArg {
|
||||
op: LowLevel,
|
||||
arg_index: Index,
|
||||
arg_index: HumanIndex,
|
||||
},
|
||||
ForeignCallArg {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
arg_index: Index,
|
||||
arg_index: HumanIndex,
|
||||
},
|
||||
FloatLiteral,
|
||||
IntLiteral,
|
||||
NumLiteral,
|
||||
StrInterpolation,
|
||||
WhenBranch {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
WhenGuard,
|
||||
ExpectCondition,
|
||||
IfCondition,
|
||||
IfBranch {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
total_branches: usize,
|
||||
},
|
||||
ElemInList {
|
||||
index: Index,
|
||||
index: HumanIndex,
|
||||
},
|
||||
RecordUpdateValue(Lowercase),
|
||||
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_collections::all::{Index, MutSet, SendMap};
|
||||
use roc_collections::all::{HumanIndex, MutSet, SendMap};
|
||||
use roc_module::called_via::{BinOp, CalledVia};
|
||||
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -350,7 +350,7 @@ fn to_expr_report<'b>(
|
||||
num_branches,
|
||||
..
|
||||
} if num_branches == 2 => alloc.concat(vec![
|
||||
alloc.keyword(if index == Index::FIRST {
|
||||
alloc.keyword(if index == HumanIndex::FIRST {
|
||||
"then"
|
||||
} else {
|
||||
"else"
|
||||
@ -1372,7 +1372,7 @@ fn to_pattern_report<'b>(
|
||||
}
|
||||
}
|
||||
PReason::WhenMatch { index } => {
|
||||
if index == Index::FIRST {
|
||||
if index == HumanIndex::FIRST {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc
|
||||
.text("The 1st pattern in this ")
|
||||
|
@ -1,7 +1,7 @@
|
||||
extern crate bumpalo;
|
||||
|
||||
use self::bumpalo::Bump;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::env::Env;
|
||||
use roc_can::expected::Expected;
|
||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||
@ -28,11 +28,12 @@ pub fn test_home() -> ModuleId {
|
||||
pub fn infer_expr(
|
||||
subs: Subs,
|
||||
problems: &mut Vec<solve::TypeError>,
|
||||
constraints: &Constraints,
|
||||
constraint: &Constraint,
|
||||
expr_var: Variable,
|
||||
) -> (Content, Subs) {
|
||||
let env = solve::Env::default();
|
||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
||||
let (solved, _) = solve::run(constraints, &env, problems, subs, constraint);
|
||||
|
||||
let content = solved
|
||||
.inner()
|
||||
@ -96,6 +97,7 @@ pub struct CanExprOut {
|
||||
pub var_store: VarStore,
|
||||
pub var: Variable,
|
||||
pub constraint: Constraint,
|
||||
pub constraints: Constraints,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -152,7 +154,9 @@ pub fn can_expr_with<'a>(
|
||||
&loc_expr.value,
|
||||
);
|
||||
|
||||
let mut constraints = Constraints::new();
|
||||
let constraint = constrain_expr(
|
||||
&mut constraints,
|
||||
&roc_constrain::expr::Env {
|
||||
rigids: ImMap::default(),
|
||||
home,
|
||||
@ -174,7 +178,7 @@ pub fn can_expr_with<'a>(
|
||||
|
||||
//load builtin values
|
||||
let (_introduced_rigids, constraint) =
|
||||
constrain_imported_values(imports, constraint, &mut var_store);
|
||||
constrain_imported_values(&mut constraints, imports, constraint, &mut var_store);
|
||||
|
||||
let mut all_ident_ids = MutMap::default();
|
||||
|
||||
@ -200,6 +204,7 @@ pub fn can_expr_with<'a>(
|
||||
interns,
|
||||
var,
|
||||
constraint,
|
||||
constraints,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,7 @@ mod test_reporting {
|
||||
output,
|
||||
var_store,
|
||||
var,
|
||||
constraints,
|
||||
constraint,
|
||||
home,
|
||||
interns,
|
||||
@ -79,7 +80,8 @@ mod test_reporting {
|
||||
}
|
||||
|
||||
let mut unify_problems = Vec::new();
|
||||
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||
let (_content, mut subs) =
|
||||
infer_expr(subs, &mut unify_problems, &constraints, &constraint, var);
|
||||
|
||||
name_all_type_vars(var, &mut subs);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user