mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Move constrain and its deps into their own crates
This commit is contained in:
parent
758de2e7bf
commit
908e485fca
58 changed files with 633 additions and 422 deletions
71
compiler/constrain/src/builtins.rs
Normal file
71
compiler/constrain/src/builtins.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::LetConstraint;
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
let expected_literal = ForReason(reason, Type::Apply(Symbol::INT_INT, vec![]), region);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, region),
|
||||
Eq(num_type, expected, region),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
let expected_literal = ForReason(reason, Type::Apply(Symbol::FLOAT_FLOAT, vec![]), region);
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, region),
|
||||
Eq(num_type, expected, 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(),
|
||||
def_aliases: SendMap::default(),
|
||||
defs_constraint: constraint,
|
||||
ret_constraint: Constraint::True,
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn builtin_type(symbol: Symbol, args: Vec<Type>) -> Type {
|
||||
Type::Apply(symbol, args)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn empty_list_type(var: Variable) -> Type {
|
||||
list_type(Type::Variable(var))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn list_type(typ: Type) -> Type {
|
||||
builtin_type(Symbol::LIST_LIST, vec![typ])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn str_type() -> Type {
|
||||
builtin_type(Symbol::STR_STR, Vec::new())
|
||||
}
|
1029
compiler/constrain/src/expr.rs
Normal file
1029
compiler/constrain/src/expr.rs
Normal file
File diff suppressed because it is too large
Load diff
17
compiler/constrain/src/lib.rs
Normal file
17
compiler/constrain/src/lib.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
// I'm skeptical that clippy:large_enum_variant is a good lint to have globally enabled.
|
||||
//
|
||||
// It warns about a performance problem where the only quick remediation is
|
||||
// to allocate more on the heap, which has lots of tradeoffs - including making it
|
||||
// long-term unclear which allocations *need* to happen for compilation's sake
|
||||
// (e.g. recursive structures) versus those which were only added to appease clippy.
|
||||
//
|
||||
// Effectively optimizing data struture memory layout isn't a quick fix,
|
||||
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
|
||||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod builtins;
|
||||
pub mod expr;
|
||||
pub mod module;
|
||||
pub mod pattern;
|
||||
pub mod uniqueness;
|
331
compiler/constrain/src/module.rs
Normal file
331
compiler/constrain/src/module.rs
Normal file
|
@ -0,0 +1,331 @@
|
|||
use crate::expr::constrain_decls;
|
||||
use roc_builtins::all::Mode;
|
||||
use roc_can::constraint::{Constraint, LetConstraint};
|
||||
use roc_can::def::Declaration;
|
||||
use roc_collections::all::{ImMap, MutMap, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::Located;
|
||||
use roc_types::boolean_algebra::{Atom, Bool};
|
||||
use roc_types::solved_types::{BuiltinAlias, SolvedAtom, SolvedType};
|
||||
use roc_types::subs::{VarId, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Type};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn constrain_module(
|
||||
home: ModuleId,
|
||||
mode: Mode,
|
||||
decls: &[Declaration],
|
||||
aliases: &MutMap<Symbol, Alias>,
|
||||
var_store: &VarStore,
|
||||
) -> Constraint {
|
||||
use Mode::*;
|
||||
|
||||
let mut send_aliases = SendMap::default();
|
||||
|
||||
for (symbol, alias) in aliases {
|
||||
send_aliases.insert(*symbol, alias.clone());
|
||||
}
|
||||
|
||||
match mode {
|
||||
Standard => constrain_decls(home, decls, send_aliases),
|
||||
Uniqueness => crate::uniqueness::constrain_decls(home, decls, send_aliases, var_store),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import<'a> {
|
||||
pub loc_symbol: Located<Symbol>,
|
||||
pub solved_type: &'a SolvedType,
|
||||
}
|
||||
|
||||
pub fn constrain_imported_values(
|
||||
imports: Vec<Import<'_>>,
|
||||
body_con: Constraint,
|
||||
var_store: &VarStore,
|
||||
) -> Constraint {
|
||||
use Constraint::*;
|
||||
let mut def_types = SendMap::default();
|
||||
let mut rigid_vars = Vec::new();
|
||||
|
||||
for import in imports {
|
||||
let mut free_vars = FreeVars::default();
|
||||
let loc_symbol = import.loc_symbol;
|
||||
|
||||
// an imported symbol can be either an alias or a value
|
||||
match import.solved_type {
|
||||
SolvedType::Alias(symbol, _, _) if symbol == &loc_symbol.value => {
|
||||
// do nothing, in the future the alias definitions should not be in the list of imported values
|
||||
}
|
||||
_ => {
|
||||
let typ = to_type(&import.solved_type, &mut free_vars, var_store);
|
||||
|
||||
def_types.insert(
|
||||
loc_symbol.value,
|
||||
Located {
|
||||
region: loc_symbol.region,
|
||||
value: typ,
|
||||
},
|
||||
);
|
||||
|
||||
for (_, var) in free_vars.named_vars {
|
||||
rigid_vars.push(var);
|
||||
}
|
||||
|
||||
// Variables can lose their name during type inference. But the unnamed
|
||||
// variables are still part of a signature, and thus must be treated as rigids here!
|
||||
for (_, var) in free_vars.unnamed_vars {
|
||||
rigid_vars.push(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars,
|
||||
flex_vars: Vec::new(),
|
||||
def_types,
|
||||
def_aliases: SendMap::default(),
|
||||
defs_constraint: True,
|
||||
ret_constraint: body_con,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn load_builtin_aliases(
|
||||
aliases: &MutMap<Symbol, BuiltinAlias>,
|
||||
body_con: Constraint,
|
||||
var_store: &VarStore,
|
||||
) -> Constraint {
|
||||
use Constraint::*;
|
||||
|
||||
// Load all builtin aliases.
|
||||
// TODO load only the ones actually used in this module
|
||||
let mut def_aliases = SendMap::default();
|
||||
|
||||
for (symbol, builtin_alias) in aliases {
|
||||
let mut free_vars = FreeVars::default();
|
||||
|
||||
let actual = to_type(&builtin_alias.typ, &mut free_vars, var_store);
|
||||
|
||||
let mut vars = Vec::with_capacity(builtin_alias.vars.len());
|
||||
|
||||
for (loc_lowercase, index) in builtin_alias.vars.iter().zip(1..) {
|
||||
let var = free_vars
|
||||
.unnamed_vars
|
||||
.get(&VarId::from_u32(index))
|
||||
.expect("var_id was not instantiated (is it phantom?)");
|
||||
|
||||
vars.push(Located::at(
|
||||
loc_lowercase.region,
|
||||
(loc_lowercase.value.clone(), *var),
|
||||
));
|
||||
}
|
||||
|
||||
let alias = Alias {
|
||||
vars,
|
||||
region: builtin_alias.region,
|
||||
typ: actual,
|
||||
};
|
||||
|
||||
def_aliases.insert(*symbol, alias);
|
||||
}
|
||||
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: Vec::new(),
|
||||
def_types: SendMap::default(),
|
||||
def_aliases,
|
||||
defs_constraint: True,
|
||||
ret_constraint: body_con,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FreeVars {
|
||||
pub named_vars: ImMap<Lowercase, Variable>,
|
||||
pub unnamed_vars: ImMap<VarId, Variable>,
|
||||
}
|
||||
|
||||
pub fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &VarStore) -> Type {
|
||||
use roc_types::solved_types::SolvedType::*;
|
||||
|
||||
match solved_type {
|
||||
Func(args, ret) => {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
new_args.push(to_type(&arg, free_vars, var_store));
|
||||
}
|
||||
|
||||
let new_ret = to_type(&ret, free_vars, var_store);
|
||||
|
||||
Type::Function(new_args, Box::new(new_ret))
|
||||
}
|
||||
Apply(symbol, args) => {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
new_args.push(to_type(&arg, free_vars, var_store));
|
||||
}
|
||||
|
||||
Type::Apply(*symbol, new_args)
|
||||
}
|
||||
Rigid(lowercase) => {
|
||||
if let Some(var) = free_vars.named_vars.get(&lowercase) {
|
||||
Type::Variable(*var)
|
||||
} else {
|
||||
let var = var_store.fresh();
|
||||
free_vars.named_vars.insert(lowercase.clone(), var);
|
||||
Type::Variable(var)
|
||||
}
|
||||
}
|
||||
Flex(var_id) => {
|
||||
if let Some(var) = free_vars.unnamed_vars.get(&var_id) {
|
||||
Type::Variable(*var)
|
||||
} else {
|
||||
let var = var_store.fresh();
|
||||
free_vars.unnamed_vars.insert(*var_id, var);
|
||||
|
||||
Type::Variable(var)
|
||||
}
|
||||
}
|
||||
Wildcard => Type::Variable(var_store.fresh()),
|
||||
Record { fields, ext } => {
|
||||
let mut new_fields = SendMap::default();
|
||||
|
||||
for (label, typ) in fields {
|
||||
new_fields.insert(label.clone(), to_type(&typ, free_vars, var_store));
|
||||
}
|
||||
|
||||
Type::Record(new_fields, Box::new(to_type(ext, free_vars, var_store)))
|
||||
}
|
||||
EmptyRecord => Type::EmptyRec,
|
||||
EmptyTagUnion => Type::EmptyTagUnion,
|
||||
TagUnion(tags, ext) => {
|
||||
let mut new_tags = Vec::with_capacity(tags.len());
|
||||
|
||||
for (tag_name, args) in tags {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args.iter() {
|
||||
new_args.push(to_type(arg, free_vars, var_store));
|
||||
}
|
||||
|
||||
new_tags.push((tag_name.clone(), new_args));
|
||||
}
|
||||
|
||||
Type::TagUnion(new_tags, Box::new(to_type(ext, free_vars, var_store)))
|
||||
}
|
||||
RecursiveTagUnion(rec_var_id, tags, ext) => {
|
||||
let mut new_tags = Vec::with_capacity(tags.len());
|
||||
|
||||
for (tag_name, args) in tags {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args.iter() {
|
||||
new_args.push(to_type(arg, free_vars, var_store));
|
||||
}
|
||||
|
||||
new_tags.push((tag_name.clone(), new_args));
|
||||
}
|
||||
|
||||
let rec_var = free_vars
|
||||
.unnamed_vars
|
||||
.get(rec_var_id)
|
||||
.expect("rec var not in unnamed vars");
|
||||
|
||||
Type::RecursiveTagUnion(
|
||||
*rec_var,
|
||||
new_tags,
|
||||
Box::new(to_type(ext, free_vars, var_store)),
|
||||
)
|
||||
}
|
||||
Boolean(solved_free, solved_rest) => {
|
||||
let free = to_atom(solved_free, free_vars, var_store);
|
||||
let mut rest = Vec::with_capacity(solved_rest.len());
|
||||
|
||||
for solved_atom in solved_rest {
|
||||
rest.push(to_atom(solved_atom, free_vars, var_store));
|
||||
}
|
||||
|
||||
Type::Boolean(Bool::from_parts(free, rest))
|
||||
}
|
||||
Alias(symbol, solved_type_variables, solved_actual) => {
|
||||
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||
|
||||
for (lowercase, solved_arg) in solved_type_variables {
|
||||
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
||||
}
|
||||
|
||||
let actual = to_type(solved_actual, free_vars, var_store);
|
||||
|
||||
Type::Alias(*symbol, type_variables, Box::new(actual))
|
||||
}
|
||||
Error => {
|
||||
panic!("TODO convert from SolvedType::Error to Type somehow");
|
||||
}
|
||||
Erroneous(problem) => Type::Erroneous(problem.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_atom(solved_atom: &SolvedAtom, free_vars: &mut FreeVars, var_store: &VarStore) -> Atom {
|
||||
match solved_atom {
|
||||
SolvedAtom::Zero => Atom::Zero,
|
||||
SolvedAtom::One => Atom::One,
|
||||
SolvedAtom::Variable(var_id) => {
|
||||
if let Some(var) = free_vars.unnamed_vars.get(&var_id) {
|
||||
Atom::Variable(*var)
|
||||
} else {
|
||||
let var = var_store.fresh();
|
||||
free_vars.unnamed_vars.insert(*var_id, var);
|
||||
|
||||
Atom::Variable(var)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constrain_imported_aliases(
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
body_con: Constraint,
|
||||
var_store: &VarStore,
|
||||
) -> Constraint {
|
||||
use Constraint::*;
|
||||
let mut def_aliases = SendMap::default();
|
||||
|
||||
for (symbol, imported_alias) in aliases {
|
||||
let mut vars = Vec::with_capacity(imported_alias.vars.len());
|
||||
let mut substitution = ImMap::default();
|
||||
|
||||
for Located {
|
||||
region,
|
||||
value: (lowercase, old_var),
|
||||
} in &imported_alias.vars
|
||||
{
|
||||
let new_var = var_store.fresh();
|
||||
vars.push(Located::at(*region, (lowercase.clone(), new_var)));
|
||||
substitution.insert(*old_var, Type::Variable(new_var));
|
||||
}
|
||||
|
||||
let mut actual = imported_alias.typ.clone();
|
||||
|
||||
actual.substitute(&substitution);
|
||||
|
||||
let alias = Alias {
|
||||
vars,
|
||||
region: imported_alias.region,
|
||||
typ: actual,
|
||||
};
|
||||
|
||||
def_aliases.insert(symbol, alias);
|
||||
}
|
||||
|
||||
Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: Vec::new(),
|
||||
def_types: SendMap::default(),
|
||||
def_aliases,
|
||||
defs_constraint: True,
|
||||
ret_constraint: body_con,
|
||||
}))
|
||||
}
|
231
compiler/constrain/src/pattern.rs
Normal file
231
compiler/constrain/src/pattern.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
use crate::builtins;
|
||||
use roc_can::constraint::Constraint;
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::pattern::Pattern::{self, *};
|
||||
use roc_can::pattern::RecordDestruct;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{PatternCategory, Type};
|
||||
|
||||
pub struct PatternState {
|
||||
pub headers: SendMap<Symbol, Located<Type>>,
|
||||
pub vars: Vec<Variable>,
|
||||
pub constraints: Vec<Constraint>,
|
||||
}
|
||||
|
||||
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
||||
/// annotation in the headers. Normally
|
||||
///
|
||||
/// x = 4
|
||||
///
|
||||
/// Would add `x => <42>` to the headers (i.e., symbol points to a type variable). If the
|
||||
/// definition has an annotation, we instead now add `x => Int`.
|
||||
pub fn headers_from_annotation(
|
||||
pattern: &Pattern,
|
||||
annotation: &Located<Type>,
|
||||
) -> Option<SendMap<Symbol, Located<Type>>> {
|
||||
let mut headers = SendMap::default();
|
||||
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
|
||||
// in such incorrect cases we don't put the full annotation in headers, just a variable, and let
|
||||
// inference generate a proper error.
|
||||
let is_structurally_valid = headers_from_annotation_help(pattern, annotation, &mut headers);
|
||||
|
||||
if is_structurally_valid {
|
||||
Some(headers)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn headers_from_annotation_help(
|
||||
pattern: &Pattern,
|
||||
annotation: &Located<Type>,
|
||||
headers: &mut SendMap<Symbol, Located<Type>>,
|
||||
) -> bool {
|
||||
match pattern {
|
||||
Identifier(symbol) => {
|
||||
headers.insert(symbol.clone(), annotation.clone());
|
||||
true
|
||||
}
|
||||
Underscore
|
||||
| Shadowed(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure(_, destructs) => match annotation.value.shallow_dealias() {
|
||||
Type::Record(fields, _) => {
|
||||
for destruct in destructs {
|
||||
// NOTE ignores the .guard field.
|
||||
if let Some(field_type) = fields.get(&destruct.value.label) {
|
||||
headers.insert(
|
||||
destruct.value.symbol.clone(),
|
||||
Located::at(annotation.region, field_type.clone()),
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
Type::EmptyRec => destructs.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
|
||||
AppliedTag(_, tag_name, arguments) => match annotation.value.shallow_dealias() {
|
||||
Type::TagUnion(tags, _) => {
|
||||
if let Some((_, arg_types)) = tags.iter().find(|(name, _)| name == tag_name) {
|
||||
if !arguments.len() == arg_types.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
arguments
|
||||
.iter()
|
||||
.zip(arg_types.iter())
|
||||
.all(|(arg_pattern, arg_type)| {
|
||||
headers_from_annotation_help(
|
||||
&arg_pattern.1.value,
|
||||
&Located::at(annotation.region, arg_type.clone()),
|
||||
headers,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// This accepts PatternState (rather than returning it) so that the caller can
|
||||
/// intiialize the Vecs in PatternState using with_capacity
|
||||
/// based on its knowledge of their lengths.
|
||||
pub fn constrain_pattern(
|
||||
pattern: &Pattern,
|
||||
region: Region,
|
||||
expected: PExpected<Type>,
|
||||
state: &mut PatternState,
|
||||
) {
|
||||
match pattern {
|
||||
Underscore | UnsupportedPattern(_) => {
|
||||
// Neither the _ pattern nor erroneous ones add any constraints.
|
||||
}
|
||||
Identifier(symbol) => {
|
||||
state.headers.insert(
|
||||
symbol.clone(),
|
||||
Located {
|
||||
region,
|
||||
value: expected.get_type(),
|
||||
},
|
||||
);
|
||||
}
|
||||
IntLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Int,
|
||||
builtins::builtin_type(Symbol::INT_INT, vec![]),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
FloatLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
builtins::builtin_type(Symbol::FLOAT_FLOAT, vec![]),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
StrLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Str,
|
||||
builtins::str_type(),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
RecordDestructure(ext_var, patterns) => {
|
||||
state.vars.push(*ext_var);
|
||||
let ext_type = Type::Variable(*ext_var);
|
||||
|
||||
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
|
||||
|
||||
for Located {
|
||||
value:
|
||||
RecordDestruct {
|
||||
var,
|
||||
label,
|
||||
symbol,
|
||||
guard,
|
||||
},
|
||||
..
|
||||
} in patterns
|
||||
{
|
||||
let pat_type = Type::Variable(*var);
|
||||
let expected = PExpected::NoExpectation(pat_type.clone());
|
||||
|
||||
if !state.headers.contains_key(&symbol) {
|
||||
state
|
||||
.headers
|
||||
.insert(symbol.clone(), Located::at(region, pat_type.clone()));
|
||||
}
|
||||
|
||||
field_types.insert(label.clone(), pat_type.clone());
|
||||
|
||||
if let Some((guard_var, loc_guard)) = guard {
|
||||
state.constraints.push(Constraint::Eq(
|
||||
Type::Variable(*guard_var),
|
||||
Expected::NoExpectation(pat_type.clone()),
|
||||
region,
|
||||
));
|
||||
state.vars.push(*guard_var);
|
||||
|
||||
constrain_pattern(&loc_guard.value, loc_guard.region, expected, state);
|
||||
}
|
||||
|
||||
state.vars.push(*var);
|
||||
}
|
||||
|
||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||
let record_con =
|
||||
Constraint::Pattern(region, PatternCategory::Record, record_type, expected);
|
||||
|
||||
state.constraints.push(record_con);
|
||||
}
|
||||
AppliedTag(ext_var, tag_name, patterns) => {
|
||||
let mut argument_types = Vec::with_capacity(patterns.len());
|
||||
for (pattern_var, loc_pattern) in patterns {
|
||||
state.vars.push(*pattern_var);
|
||||
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
argument_types.push(pattern_type.clone());
|
||||
|
||||
let expected = PExpected::NoExpectation(pattern_type);
|
||||
constrain_pattern(&loc_pattern.value, loc_pattern.region, expected, state);
|
||||
}
|
||||
|
||||
let tag_con = Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Ctor(tag_name.clone()),
|
||||
Type::TagUnion(
|
||||
vec![(tag_name.clone(), argument_types)],
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
),
|
||||
expected,
|
||||
);
|
||||
|
||||
state.vars.push(*ext_var);
|
||||
state.constraints.push(tag_con);
|
||||
}
|
||||
Shadowed(_, _) => {
|
||||
panic!("TODO constrain Shadowed pattern");
|
||||
}
|
||||
}
|
||||
}
|
1977
compiler/constrain/src/uniqueness.rs
Normal file
1977
compiler/constrain/src/uniqueness.rs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue