mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge branch 'trunk' into ct/format-new-line-before-return
This commit is contained in:
commit
1a7b74cd3a
14 changed files with 419 additions and 57 deletions
|
@ -1,5 +1,6 @@
|
||||||
use crate::can::def::{can_defs_with_return, Def, Info};
|
use crate::can::def::{can_defs_with_return, Def, Info};
|
||||||
use crate::can::env::Env;
|
use crate::can::env::Env;
|
||||||
|
use crate::can::ident::Lowercase;
|
||||||
use crate::can::num::{
|
use crate::can::num::{
|
||||||
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
||||||
int_expr_from_result,
|
int_expr_from_result,
|
||||||
|
@ -637,8 +638,37 @@ pub fn canonicalize_expr(
|
||||||
|
|
||||||
(expr, output, And(constraints))
|
(expr, output, And(constraints))
|
||||||
}
|
}
|
||||||
ast::Expr::Access(_, _)
|
ast::Expr::Access(record_expr, field) => {
|
||||||
| ast::Expr::AccessorFunction(_)
|
let ext_var = var_store.fresh();
|
||||||
|
let field_var = var_store.fresh();
|
||||||
|
let ext_type = Type::Variable(ext_var);
|
||||||
|
let field_type = Type::Variable(field_var);
|
||||||
|
|
||||||
|
let mut rec_field_types = SendMap::default();
|
||||||
|
|
||||||
|
rec_field_types.insert(Lowercase::from_unqualified_ident(field), field_type.clone());
|
||||||
|
|
||||||
|
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
|
||||||
|
let record_expected = Expected::NoExpectation(record_type);
|
||||||
|
|
||||||
|
let (loc_expr, output, mut constraint) = canonicalize_expr(
|
||||||
|
&ImMap::default(),
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
scope,
|
||||||
|
region,
|
||||||
|
record_expr,
|
||||||
|
record_expected,
|
||||||
|
);
|
||||||
|
|
||||||
|
constraint = exists(
|
||||||
|
vec![field_var, ext_var],
|
||||||
|
And(vec![constraint, Eq(field_type, expected, region)]),
|
||||||
|
);
|
||||||
|
|
||||||
|
(loc_expr.value, output, constraint)
|
||||||
|
}
|
||||||
|
ast::Expr::AccessorFunction(_)
|
||||||
| ast::Expr::If(_)
|
| ast::Expr::If(_)
|
||||||
| ast::Expr::GlobalTag(_)
|
| ast::Expr::GlobalTag(_)
|
||||||
| ast::Expr::PrivateTag(_)
|
| ast::Expr::PrivateTag(_)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod def;
|
pub mod def;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
|
pub mod ident;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod num;
|
pub mod num;
|
||||||
pub mod operator;
|
pub mod operator;
|
||||||
|
|
10
src/infer.rs
10
src/infer.rs
|
@ -3,11 +3,17 @@ use crate::collections::ImMap;
|
||||||
use crate::solve::solve;
|
use crate::solve::solve;
|
||||||
use crate::subs::{Content, Subs, Variable};
|
use crate::subs::{Content, Subs, Variable};
|
||||||
use crate::types::Constraint;
|
use crate::types::Constraint;
|
||||||
|
use crate::unify::Problems;
|
||||||
|
|
||||||
pub fn infer_expr(subs: &mut Subs, constraint: &Constraint, expr_var: Variable) -> Content {
|
pub fn infer_expr(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
constraint: &Constraint,
|
||||||
|
expr_var: Variable,
|
||||||
|
) -> Content {
|
||||||
let env: ImMap<Symbol, Variable> = ImMap::default();
|
let env: ImMap<Symbol, Variable> = ImMap::default();
|
||||||
|
|
||||||
solve(&env, subs, constraint);
|
solve(&env, problems, subs, constraint);
|
||||||
|
|
||||||
subs.get(expr_var).content
|
subs.get(expr_var).content
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::solve::solve;
|
||||||
use crate::subs::VarStore;
|
use crate::subs::VarStore;
|
||||||
use crate::subs::{Subs, Variable};
|
use crate::subs::{Subs, Variable};
|
||||||
use crate::types::Constraint;
|
use crate::types::Constraint;
|
||||||
|
use crate::unify::Problems;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -348,7 +349,12 @@ fn expose(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve_loaded(module: &Module, subs: &mut Subs, loaded_deps: LoadedDeps) {
|
pub fn solve_loaded(
|
||||||
|
module: &Module,
|
||||||
|
problems: &mut Problems,
|
||||||
|
subs: &mut Subs,
|
||||||
|
loaded_deps: LoadedDeps,
|
||||||
|
) {
|
||||||
use LoadedModule::*;
|
use LoadedModule::*;
|
||||||
|
|
||||||
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
|
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
|
||||||
|
@ -403,8 +409,8 @@ pub fn solve_loaded(module: &Module, subs: &mut Subs, loaded_deps: LoadedDeps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for constraint in constraints {
|
for constraint in constraints {
|
||||||
solve(&vars_by_symbol, subs, &constraint);
|
solve(&vars_by_symbol, problems, subs, &constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
solve(&vars_by_symbol, subs, &module.constraint);
|
solve(&vars_by_symbol, problems, subs, &module.constraint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ where
|
||||||
|
|
||||||
let next_state = state.advance_without_indenting(bytes_parsed)?;
|
let next_state = state.advance_without_indenting(bytes_parsed)?;
|
||||||
|
|
||||||
Ok((dbg!(expr), next_state))
|
Ok((expr, next_state))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
|
@ -89,11 +89,17 @@ fn find_names_needed(
|
||||||
|
|
||||||
find_names_needed(ret_var, subs, roots, root_appearances, names_taken);
|
find_names_needed(ret_var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
|
Structure(Record(_, _)) => {
|
||||||
|
panic!("TODO find_names_needed Record");
|
||||||
|
}
|
||||||
RigidVar(name) => {
|
RigidVar(name) => {
|
||||||
// User-defined names are already taken.
|
// User-defined names are already taken.
|
||||||
// We must not accidentally generate names that collide with them!
|
// We must not accidentally generate names that collide with them!
|
||||||
names_taken.insert(name.to_string());
|
names_taken.insert(name.to_string());
|
||||||
}
|
}
|
||||||
|
Alias(_, _, _, _) => {
|
||||||
|
panic!("TODO find_names_needed Alias");
|
||||||
|
}
|
||||||
Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => {
|
Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => {
|
||||||
// Errors and empty records don't need names.
|
// Errors and empty records don't need names.
|
||||||
}
|
}
|
||||||
|
@ -173,6 +179,9 @@ fn write_content(content: Content, subs: &mut Subs, buf: &mut String, parens: Pa
|
||||||
FlexVar(None) => buf.push_str(WILDCARD),
|
FlexVar(None) => buf.push_str(WILDCARD),
|
||||||
RigidVar(name) => buf.push_str(&name),
|
RigidVar(name) => buf.push_str(&name),
|
||||||
Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens),
|
Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens),
|
||||||
|
Alias(_, _, _, _) => {
|
||||||
|
panic!("TODO write_content Alias");
|
||||||
|
}
|
||||||
Error(_) => buf.push_str("<type mismatch>"),
|
Error(_) => buf.push_str("<type mismatch>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,6 +204,9 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, paren
|
||||||
),
|
),
|
||||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||||
Func(args, ret) => write_fn(args, ret, subs, buf, parens),
|
Func(args, ret) => write_fn(args, ret, subs, buf, parens),
|
||||||
|
Record(_, _) => {
|
||||||
|
panic!("TODO write_flat_type Record");
|
||||||
|
}
|
||||||
Erroneous(problem) => {
|
Erroneous(problem) => {
|
||||||
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
||||||
}
|
}
|
||||||
|
|
51
src/solve.rs
51
src/solve.rs
|
@ -3,11 +3,16 @@ use crate::collections::ImMap;
|
||||||
use crate::subs::{Content, Descriptor, FlatType, Subs, Variable};
|
use crate::subs::{Content, Descriptor, FlatType, Subs, Variable};
|
||||||
use crate::types::Constraint::{self, *};
|
use crate::types::Constraint::{self, *};
|
||||||
use crate::types::Type::{self, *};
|
use crate::types::Type::{self, *};
|
||||||
use crate::unify::unify;
|
use crate::unify::{unify, Problems};
|
||||||
|
|
||||||
type Env = ImMap<Symbol, Variable>;
|
type Env = ImMap<Symbol, Variable>;
|
||||||
|
|
||||||
pub fn solve(vars_by_symbol: &Env, subs: &mut Subs, constraint: &Constraint) {
|
pub fn solve(
|
||||||
|
vars_by_symbol: &Env,
|
||||||
|
problems: &mut Problems,
|
||||||
|
subs: &mut Subs,
|
||||||
|
constraint: &Constraint,
|
||||||
|
) {
|
||||||
match constraint {
|
match constraint {
|
||||||
True => (),
|
True => (),
|
||||||
Eq(typ, expected_type, _region) => {
|
Eq(typ, expected_type, _region) => {
|
||||||
|
@ -15,7 +20,7 @@ pub fn solve(vars_by_symbol: &Env, subs: &mut Subs, constraint: &Constraint) {
|
||||||
let actual = type_to_var(subs, typ.clone());
|
let actual = type_to_var(subs, typ.clone());
|
||||||
let expected = type_to_var(subs, expected_type.clone().get_type());
|
let expected = type_to_var(subs, expected_type.clone().get_type());
|
||||||
|
|
||||||
unify(subs, actual, expected);
|
unify(subs, problems, actual, expected);
|
||||||
}
|
}
|
||||||
Lookup(symbol, expected_type, _region) => {
|
Lookup(symbol, expected_type, _region) => {
|
||||||
// TODO use region?
|
// TODO use region?
|
||||||
|
@ -29,11 +34,11 @@ pub fn solve(vars_by_symbol: &Env, subs: &mut Subs, constraint: &Constraint) {
|
||||||
}));
|
}));
|
||||||
let expected = type_to_var(subs, expected_type.clone().get_type());
|
let expected = type_to_var(subs, expected_type.clone().get_type());
|
||||||
|
|
||||||
unify(subs, actual, expected);
|
unify(subs, problems, actual, expected);
|
||||||
}
|
}
|
||||||
And(sub_constraints) => {
|
And(sub_constraints) => {
|
||||||
for sub_constraint in sub_constraints.iter() {
|
for sub_constraint in sub_constraints.iter() {
|
||||||
solve(vars_by_symbol, subs, sub_constraint);
|
solve(vars_by_symbol, problems, subs, sub_constraint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pattern(_region, _category, typ, expected) => {
|
Pattern(_region, _category, typ, expected) => {
|
||||||
|
@ -41,18 +46,18 @@ pub fn solve(vars_by_symbol: &Env, subs: &mut Subs, constraint: &Constraint) {
|
||||||
let actual = type_to_var(subs, typ.clone());
|
let actual = type_to_var(subs, typ.clone());
|
||||||
let expected = type_to_var(subs, expected.clone().get_type());
|
let expected = type_to_var(subs, expected.clone().get_type());
|
||||||
|
|
||||||
unify(subs, actual, expected);
|
unify(subs, problems, actual, expected);
|
||||||
}
|
}
|
||||||
Let(let_con) => {
|
Let(let_con) => {
|
||||||
match &let_con.ret_constraint {
|
match &let_con.ret_constraint {
|
||||||
True => {
|
True => {
|
||||||
// If the return expression is guaranteed to solve,
|
// If the return expression is guaranteed to solve,
|
||||||
// solve the assignments themselves and move on.
|
// solve the assignments themselves and move on.
|
||||||
solve(vars_by_symbol, subs, &let_con.defs_constraint)
|
solve(vars_by_symbol, problems, subs, &let_con.defs_constraint)
|
||||||
}
|
}
|
||||||
ret_con => {
|
ret_con => {
|
||||||
// Solve the assignments' constraints first.
|
// Solve the assignments' constraints first.
|
||||||
solve(vars_by_symbol, subs, &let_con.defs_constraint);
|
solve(vars_by_symbol, problems, subs, &let_con.defs_constraint);
|
||||||
|
|
||||||
// Add a variable for each assignment to the vars_by_symbol.
|
// Add a variable for each assignment to the vars_by_symbol.
|
||||||
let mut new_vars_by_symbol = vars_by_symbol.clone();
|
let mut new_vars_by_symbol = vars_by_symbol.clone();
|
||||||
|
@ -69,7 +74,7 @@ pub fn solve(vars_by_symbol: &Env, subs: &mut Subs, constraint: &Constraint) {
|
||||||
|
|
||||||
// Now solve the body, using the new vars_by_symbol which includes
|
// Now solve the body, using the new vars_by_symbol which includes
|
||||||
// the assignments' name-to-variable mappings.
|
// the assignments' name-to-variable mappings.
|
||||||
solve(&new_vars_by_symbol, subs, &ret_con);
|
solve(&new_vars_by_symbol, problems, subs, &ret_con);
|
||||||
|
|
||||||
// TODO do an occurs check for each of the assignments!
|
// TODO do an occurs check for each of the assignments!
|
||||||
}
|
}
|
||||||
|
@ -122,6 +127,34 @@ fn type_to_variable(subs: &mut Subs, aliases: &ImMap<Box<str>, Variable>, typ: T
|
||||||
|
|
||||||
subs.fresh(Descriptor::from(content))
|
subs.fresh(Descriptor::from(content))
|
||||||
}
|
}
|
||||||
|
Record(fields, ext) => {
|
||||||
|
let mut field_vars = ImMap::default();
|
||||||
|
|
||||||
|
for (field, field_type) in fields {
|
||||||
|
field_vars.insert(field, type_to_variable(subs, aliases, field_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ext_var = type_to_variable(subs, aliases, *ext);
|
||||||
|
let content = Content::Structure(FlatType::Record(field_vars, ext_var));
|
||||||
|
|
||||||
|
subs.fresh(Descriptor::from(content))
|
||||||
|
}
|
||||||
|
Alias(home, name, args, alias_type) => {
|
||||||
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
|
let mut new_aliases = ImMap::default();
|
||||||
|
|
||||||
|
for (arg, arg_type) in args {
|
||||||
|
let arg_var = type_to_variable(subs, aliases, arg_type.clone());
|
||||||
|
|
||||||
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
|
new_aliases.insert(arg.into(), arg_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias_var = type_to_variable(subs, &new_aliases, *alias_type);
|
||||||
|
let content = Content::Alias(home, name, arg_vars, alias_var);
|
||||||
|
|
||||||
|
subs.fresh(Descriptor::from(content))
|
||||||
|
}
|
||||||
Erroneous(problem) => {
|
Erroneous(problem) => {
|
||||||
let content = Content::Structure(FlatType::Erroneous(problem));
|
let content = Content::Structure(FlatType::Erroneous(problem));
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::can::ident::{Lowercase, ModuleName, Uppercase};
|
||||||
|
use crate::collections::ImMap;
|
||||||
use crate::ena::unify::{InPlace, UnificationTable, UnifyKey};
|
use crate::ena::unify::{InPlace, UnificationTable, UnifyKey};
|
||||||
use crate::types::Problem;
|
use crate::types::Problem;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -128,6 +130,10 @@ impl Subs {
|
||||||
self.utable.new_key(value)
|
self.utable.new_key(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fresh_unnamed_flex_var(&mut self) -> Variable {
|
||||||
|
self.fresh(unnamed_flex_var().into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Unions two keys without the possibility of failure.
|
/// Unions two keys without the possibility of failure.
|
||||||
pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) {
|
pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) {
|
||||||
let l_root = self.utable.get_root_key(left);
|
let l_root = self.utable.get_root_key(left);
|
||||||
|
@ -206,6 +212,7 @@ pub enum Content {
|
||||||
/// name given in a user-written annotation
|
/// name given in a user-written annotation
|
||||||
RigidVar(Box<str>),
|
RigidVar(Box<str>),
|
||||||
Structure(FlatType),
|
Structure(FlatType),
|
||||||
|
Alias(ModuleName, Uppercase, Vec<(Lowercase, Variable)>, Variable),
|
||||||
Error(Problem),
|
Error(Problem),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +224,7 @@ pub enum FlatType {
|
||||||
args: Vec<Variable>,
|
args: Vec<Variable>,
|
||||||
},
|
},
|
||||||
Func(Vec<Variable>, Variable),
|
Func(Vec<Variable>, Variable),
|
||||||
|
Record(ImMap<Lowercase, Variable>, Variable),
|
||||||
Erroneous(Problem),
|
Erroneous(Problem),
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::can::ident::{Lowercase, ModuleName, Uppercase};
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::SendMap;
|
use crate::collections::SendMap;
|
||||||
use crate::operator::{ArgSide, BinOp};
|
use crate::operator::{ArgSide, BinOp};
|
||||||
|
@ -26,6 +27,8 @@ pub enum Type {
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
Function(Vec<Type>, Box<Type>),
|
Function(Vec<Type>, Box<Type>),
|
||||||
|
Record(SendMap<Lowercase, Type>, Box<Type>),
|
||||||
|
Alias(ModuleName, Uppercase, Vec<(Lowercase, Type)>, Box<Type>),
|
||||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||||
Apply {
|
Apply {
|
||||||
module_name: Box<str>,
|
module_name: Box<str>,
|
||||||
|
@ -82,6 +85,12 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
|
Type::Alias(_, _, _, _) => {
|
||||||
|
panic!("TODO fmt type aliases");
|
||||||
|
}
|
||||||
|
Type::Record(_, _) => {
|
||||||
|
panic!("TODO fmt record types");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
323
src/unify.rs
323
src/unify.rs
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::can::ident::{Lowercase, ModuleName, Uppercase};
|
||||||
|
use crate::collections::ImMap;
|
||||||
use crate::subs::Content::{self, *};
|
use crate::subs::Content::{self, *};
|
||||||
use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable};
|
use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable};
|
||||||
use crate::types::Problem;
|
use crate::types::Problem;
|
||||||
|
@ -9,8 +11,15 @@ struct Context {
|
||||||
second_desc: Descriptor,
|
second_desc: Descriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RecordStructure {
|
||||||
|
fields: ImMap<Lowercase, Variable>,
|
||||||
|
ext: Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Problems = Vec<Problem>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) {
|
pub fn unify(subs: &mut Subs, problems: &mut Problems, var1: Variable, var2: Variable) {
|
||||||
if !subs.equivalent(var1, var2) {
|
if !subs.equivalent(var1, var2) {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
first: var1,
|
first: var1,
|
||||||
|
@ -19,50 +28,248 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) {
|
||||||
second_desc: subs.get(var2),
|
second_desc: subs.get(var2),
|
||||||
};
|
};
|
||||||
|
|
||||||
unify_context(subs, &ctx)
|
unify_context(subs, problems, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_context(subs: &mut Subs, ctx: &Context) {
|
fn unify_context(subs: &mut Subs, problems: &mut Problems, ctx: Context) {
|
||||||
match ctx.first_desc.content {
|
match &ctx.first_desc.content {
|
||||||
FlexVar(ref opt_name) => unify_flex(subs, ctx, opt_name, &ctx.second_desc.content),
|
FlexVar(opt_name) => unify_flex(subs, problems, &ctx, opt_name, &ctx.second_desc.content),
|
||||||
RigidVar(ref name) => unify_rigid(subs, ctx, name, &ctx.second_desc.content),
|
RigidVar(name) => unify_rigid(subs, problems, &ctx, name, &ctx.second_desc.content),
|
||||||
Structure(ref flat_type) => unify_structure(subs, ctx, flat_type, &ctx.second_desc.content),
|
Structure(flat_type) => {
|
||||||
Error(ref problem) => {
|
unify_structure(subs, problems, &ctx, flat_type, &ctx.second_desc.content)
|
||||||
|
}
|
||||||
|
Alias(home, name, args, real_var) => {
|
||||||
|
unify_alias(subs, problems, &ctx, home, name, args, *real_var)
|
||||||
|
}
|
||||||
|
Error(problem) => {
|
||||||
// Error propagates. Whatever we're comparing it to doesn't matter!
|
// Error propagates. Whatever we're comparing it to doesn't matter!
|
||||||
merge(subs, ctx, Error(problem.clone()))
|
merge(subs, &ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) {
|
fn unify_alias(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
home: &ModuleName,
|
||||||
|
name: &Uppercase,
|
||||||
|
args: &[(Lowercase, Variable)],
|
||||||
|
real_var: Variable,
|
||||||
|
) {
|
||||||
|
let other_content = &ctx.second_desc.content;
|
||||||
|
|
||||||
|
match other_content {
|
||||||
|
FlexVar(_) => {
|
||||||
|
// Alias wins
|
||||||
|
merge(
|
||||||
|
subs,
|
||||||
|
&ctx,
|
||||||
|
Alias(home.clone(), name.clone(), args.to_owned(), real_var),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RigidVar(_) => unify(subs, problems, real_var, ctx.second),
|
||||||
|
Alias(other_home, other_name, other_args, other_real_var) => {
|
||||||
|
if name == other_name && home == other_home {
|
||||||
|
if args.len() == other_args.len() {
|
||||||
|
for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) {
|
||||||
|
unify(subs, problems, *l_var, *r_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(subs, &ctx, other_content.clone());
|
||||||
|
} else if args.len() > other_args.len() {
|
||||||
|
let problem = Problem::ExtraArguments;
|
||||||
|
|
||||||
|
merge(subs, &ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
} else {
|
||||||
|
let problem = Problem::MissingArguments;
|
||||||
|
|
||||||
|
merge(subs, &ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unify(subs, problems, real_var, *other_real_var)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Structure(_) => unify(subs, problems, real_var, ctx.second),
|
||||||
|
Error(problem) => {
|
||||||
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unify_structure(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
flat_type: &FlatType,
|
||||||
|
other: &Content,
|
||||||
|
) {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// If the other is flex, Structure wins!
|
// If the other is flex, Structure wins!
|
||||||
merge(subs, ctx, Structure(flat_type.clone()))
|
merge(subs, ctx, Structure(flat_type.clone()));
|
||||||
}
|
}
|
||||||
RigidVar(_) => {
|
RigidVar(_) => {
|
||||||
|
let problem = Problem::GenericMismatch;
|
||||||
// Type mismatch! Rigid can only unify with flex.
|
// Type mismatch! Rigid can only unify with flex.
|
||||||
merge(subs, ctx, Error(Problem::GenericMismatch))
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
}
|
}
|
||||||
Structure(ref other_flat_type) => {
|
Structure(ref other_flat_type) => {
|
||||||
// Unify the two flat types
|
// Unify the two flat types
|
||||||
unify_flat_type(subs, ctx, flat_type, other_flat_type)
|
unify_flat_type(subs, problems, ctx, flat_type, other_flat_type)
|
||||||
}
|
}
|
||||||
|
Alias(_, _, _, real_var) => unify(subs, problems, ctx.first, *real_var),
|
||||||
Error(problem) => {
|
Error(problem) => {
|
||||||
// Error propagates.
|
// Error propagates.
|
||||||
merge(subs, ctx, Error(problem.clone()))
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unify_record(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
rec1: RecordStructure,
|
||||||
|
rec2: RecordStructure,
|
||||||
|
) {
|
||||||
|
let fields1 = rec1.fields;
|
||||||
|
let fields2 = rec2.fields;
|
||||||
|
let shared_fields = fields1
|
||||||
|
.clone()
|
||||||
|
.intersection_with(fields2.clone(), |one, two| (one, two));
|
||||||
|
let unique_fields1 = fields1.clone().difference(fields2.clone());
|
||||||
|
let unique_fields2 = fields2.difference(fields1);
|
||||||
|
|
||||||
|
if unique_fields1.is_empty() {
|
||||||
|
if unique_fields2.is_empty() {
|
||||||
|
unify(subs, problems, rec1.ext, rec2.ext);
|
||||||
|
unify_shared_fields(
|
||||||
|
subs,
|
||||||
|
problems,
|
||||||
|
ctx,
|
||||||
|
shared_fields,
|
||||||
|
ImMap::default(),
|
||||||
|
rec1.ext,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let flat_type = FlatType::Record(unique_fields2, rec2.ext);
|
||||||
|
let sub_record = subs.fresh(Structure(flat_type).into());
|
||||||
|
|
||||||
|
unify(subs, problems, rec1.ext, sub_record);
|
||||||
|
|
||||||
|
unify_shared_fields(
|
||||||
|
subs,
|
||||||
|
problems,
|
||||||
|
ctx,
|
||||||
|
shared_fields,
|
||||||
|
ImMap::default(),
|
||||||
|
sub_record,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if unique_fields2.is_empty() {
|
||||||
|
let flat_type = FlatType::Record(unique_fields1, rec1.ext);
|
||||||
|
let sub_record = subs.fresh(Structure(flat_type).into());
|
||||||
|
|
||||||
|
unify(subs, problems, sub_record, rec2.ext);
|
||||||
|
|
||||||
|
unify_shared_fields(
|
||||||
|
subs,
|
||||||
|
problems,
|
||||||
|
ctx,
|
||||||
|
shared_fields,
|
||||||
|
ImMap::default(),
|
||||||
|
sub_record,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let other_fields = unique_fields1.clone().union(unique_fields2.clone());
|
||||||
|
let ext = subs.fresh_unnamed_flex_var();
|
||||||
|
let flat_type1 = FlatType::Record(unique_fields1, rec1.ext);
|
||||||
|
let sub1 = subs.fresh(Structure(flat_type1).into());
|
||||||
|
let flat_type2 = FlatType::Record(unique_fields2, rec2.ext);
|
||||||
|
let sub2 = subs.fresh(Structure(flat_type2).into());
|
||||||
|
|
||||||
|
unify(subs, problems, rec1.ext, sub2);
|
||||||
|
unify(subs, problems, sub1, rec2.ext);
|
||||||
|
|
||||||
|
unify_shared_fields(subs, problems, ctx, shared_fields, other_fields, ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unify_shared_fields(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
shared_fields: ImMap<Lowercase, (Variable, Variable)>,
|
||||||
|
other_fields: ImMap<Lowercase, Variable>,
|
||||||
|
ext: Variable,
|
||||||
|
) {
|
||||||
|
let mut matching_fields = ImMap::default();
|
||||||
|
let num_shared_fields = shared_fields.len();
|
||||||
|
|
||||||
|
for (name, (actual, expected)) in shared_fields {
|
||||||
|
let prev_problem_count = problems.len();
|
||||||
|
|
||||||
|
// TODO another way to do this might be to pass around a problems vec
|
||||||
|
// and check to see if its length increased after doing this unification.
|
||||||
|
unify(subs, problems, actual, expected);
|
||||||
|
|
||||||
|
if problems.len() == prev_problem_count {
|
||||||
|
matching_fields.insert(name, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_shared_fields == matching_fields.len() {
|
||||||
|
let flat_type = FlatType::Record(matching_fields.union(other_fields), ext);
|
||||||
|
|
||||||
|
merge(subs, ctx, Structure(flat_type));
|
||||||
|
} else {
|
||||||
|
let problem = Problem::GenericMismatch;
|
||||||
|
|
||||||
|
// Type mismatch! Rigid can only unify with flex.
|
||||||
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) {
|
fn unify_flat_type(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
left: &FlatType,
|
||||||
|
right: &FlatType,
|
||||||
|
) {
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
|
(EmptyRecord, EmptyRecord) => {
|
||||||
|
merge(subs, ctx, Structure(left.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
(Record(fields, ext), EmptyRecord) if fields.is_empty() => {
|
||||||
|
unify(subs, problems, *ext, ctx.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
(EmptyRecord, Record(fields, ext)) if fields.is_empty() => {
|
||||||
|
unify(subs, problems, ctx.first, *ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
(Record(fields1, ext1), Record(fields2, ext2)) => {
|
||||||
|
let rec1 = gather_fields(subs, problems, fields1.clone(), *ext1);
|
||||||
|
let rec2 = gather_fields(subs, problems, fields2.clone(), *ext2);
|
||||||
|
|
||||||
|
unify_record(subs, problems, ctx, rec1, rec2)
|
||||||
|
}
|
||||||
(
|
(
|
||||||
Apply {
|
Apply {
|
||||||
module_name: l_module_name,
|
module_name: l_module_name,
|
||||||
|
@ -75,7 +282,7 @@ fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &Flat
|
||||||
args: r_args,
|
args: r_args,
|
||||||
},
|
},
|
||||||
) if l_module_name == r_module_name && l_type_name == r_type_name => {
|
) if l_module_name == r_module_name && l_type_name == r_type_name => {
|
||||||
unify_zip(subs, l_args.iter(), r_args.iter());
|
unify_zip(subs, problems, l_args.iter(), r_args.iter());
|
||||||
|
|
||||||
merge(
|
merge(
|
||||||
subs,
|
subs,
|
||||||
|
@ -85,64 +292,110 @@ fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &Flat
|
||||||
name: (*r_type_name).clone(),
|
name: (*r_type_name).clone(),
|
||||||
args: (*r_args).clone(),
|
args: (*r_args).clone(),
|
||||||
}),
|
}),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
(Func(l_args, l_ret), Func(r_args, r_ret)) => {
|
(Func(l_args, l_ret), Func(r_args, r_ret)) => {
|
||||||
if l_args.len() == r_args.len() {
|
if l_args.len() == r_args.len() {
|
||||||
unify_zip(subs, l_args.iter(), r_args.iter());
|
unify_zip(subs, problems, l_args.iter(), r_args.iter());
|
||||||
unify(subs, *l_ret, *r_ret);
|
unify(subs, problems, *l_ret, *r_ret);
|
||||||
|
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)));
|
||||||
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)))
|
|
||||||
} else if l_args.len() > r_args.len() {
|
} else if l_args.len() > r_args.len() {
|
||||||
merge(subs, ctx, Error(Problem::ExtraArguments))
|
merge(subs, ctx, Error(Problem::ExtraArguments));
|
||||||
} else {
|
} else {
|
||||||
merge(subs, ctx, Error(Problem::MissingArguments))
|
merge(subs, ctx, Error(Problem::MissingArguments));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => merge(subs, ctx, Error(Problem::GenericMismatch)),
|
_ => {
|
||||||
|
let problem = Problem::GenericMismatch;
|
||||||
|
|
||||||
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_zip<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I)
|
fn unify_zip<'a, I>(subs: &mut Subs, problems: &mut Problems, left_iter: I, right_iter: I)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a Variable>,
|
I: Iterator<Item = &'a Variable>,
|
||||||
{
|
{
|
||||||
for (&l_var, &r_var) in left_iter.zip(right_iter) {
|
for (&l_var, &r_var) in left_iter.zip(right_iter) {
|
||||||
unify(subs, l_var, r_var);
|
unify(subs, problems, l_var, r_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) {
|
fn unify_rigid(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
name: &str,
|
||||||
|
other: &Content,
|
||||||
|
) {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// If the other is flex, rigid wins!
|
// If the other is flex, rigid wins!
|
||||||
merge(subs, ctx, RigidVar(name.into()))
|
merge(subs, ctx, RigidVar(name.into()));
|
||||||
}
|
}
|
||||||
RigidVar(_) | Structure(_) => {
|
RigidVar(_) | Structure(_) => {
|
||||||
// Type mismatch! Rigid can only unify with flex, even if the
|
// Type mismatch! Rigid can only unify with flex, even if the
|
||||||
// rigid names are the same.
|
// rigid names are the same.
|
||||||
merge(subs, ctx, Error(Problem::GenericMismatch))
|
merge(subs, ctx, Error(Problem::GenericMismatch));
|
||||||
|
}
|
||||||
|
Alias(_, _, _, _) => {
|
||||||
|
panic!("TODO unify_rigid Alias");
|
||||||
}
|
}
|
||||||
Error(problem) => {
|
Error(problem) => {
|
||||||
// Error propagates.
|
// Error propagates.
|
||||||
merge(subs, ctx, Error(problem.clone()))
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option<Box<str>>, other: &Content) {
|
fn unify_flex(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
ctx: &Context,
|
||||||
|
opt_name: &Option<Box<str>>,
|
||||||
|
other: &Content,
|
||||||
|
) {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(None) => {
|
FlexVar(None) => {
|
||||||
// If both are flex, and only left has a name, keep the name around.
|
// If both are flex, and only left has a name, keep the name around.
|
||||||
merge(subs, ctx, FlexVar(opt_name.clone()))
|
merge(subs, ctx, FlexVar(opt_name.clone()));
|
||||||
}
|
}
|
||||||
FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Error(_) => {
|
FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) => {
|
||||||
// In all other cases, if left is flex, defer to right.
|
// In all other cases, if left is flex, defer to right.
|
||||||
// (This includes using right's name if both are flex and named.)
|
// (This includes using right's name if both are flex and named.)
|
||||||
merge(subs, ctx, other.clone())
|
merge(subs, ctx, other.clone());
|
||||||
}
|
}
|
||||||
|
Error(problem) => {
|
||||||
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
problems.push(problem.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gather_fields(
|
||||||
|
subs: &mut Subs,
|
||||||
|
problems: &mut Problems,
|
||||||
|
fields: ImMap<Lowercase, Variable>,
|
||||||
|
var: Variable,
|
||||||
|
) -> RecordStructure {
|
||||||
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
match subs.get(var).content {
|
||||||
|
Structure(Record(sub_fields, sub_ext)) => {
|
||||||
|
gather_fields(subs, problems, fields.union(sub_fields), sub_ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
Alias(_, _, _, var) => {
|
||||||
|
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
|
||||||
|
gather_fields(subs, problems, fields, var)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => RecordStructure { fields, ext: var },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ mod test_gen {
|
||||||
($src:expr, $expected:expr, $ty:ty) => {
|
($src:expr, $expected:expr, $ty:ty) => {
|
||||||
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store.into());
|
||||||
let content = infer_expr(&mut subs, &constraint, variable);
|
let mut unify_problems = Vec::new();
|
||||||
|
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
|
|
|
@ -20,7 +20,8 @@ mod test_infer {
|
||||||
fn infer_eq(src: &str, expected: &str) {
|
fn infer_eq(src: &str, expected: &str) {
|
||||||
let (_, _output, _, var_store, variable, constraint) = can_expr(src);
|
let (_, _output, _, var_store, variable, constraint) = can_expr(src);
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store.into());
|
||||||
let content = infer_expr(&mut subs, &constraint, variable);
|
let mut unify_problems = Vec::new();
|
||||||
|
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||||
|
|
||||||
name_all_type_vars(variable, &mut subs);
|
name_all_type_vars(variable, &mut subs);
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,8 @@ mod test_load {
|
||||||
|
|
||||||
assert_eq!(module.name, Some("WithBuiltins".into()));
|
assert_eq!(module.name, Some("WithBuiltins".into()));
|
||||||
|
|
||||||
solve_loaded(&module, &mut subs, deps);
|
let mut unify_problems = Vec::new();
|
||||||
|
solve_loaded(&module, &mut unify_problems, &mut subs, deps);
|
||||||
|
|
||||||
let expected_types = hashmap! {
|
let expected_types = hashmap! {
|
||||||
"WithBuiltins.floatTest" => "Float",
|
"WithBuiltins.floatTest" => "Float",
|
||||||
|
|
|
@ -33,8 +33,9 @@ mod test_infer_uniq {
|
||||||
let mut subs1 = Subs::new(var_store1.into());
|
let mut subs1 = Subs::new(var_store1.into());
|
||||||
let mut subs2 = Subs::new(var_store2.into());
|
let mut subs2 = Subs::new(var_store2.into());
|
||||||
|
|
||||||
let content1 = infer_expr(&mut subs1, &constraint1, variable1);
|
let mut unify_problems = Vec::new();
|
||||||
let content2 = infer_expr(&mut subs2, &constraint2, variable2);
|
let content1 = infer_expr(&mut subs1, &mut unify_problems, &constraint1, variable1);
|
||||||
|
let content2 = infer_expr(&mut subs2, &mut unify_problems, &constraint2, variable2);
|
||||||
|
|
||||||
name_all_type_vars(variable1, &mut subs1);
|
name_all_type_vars(variable1, &mut subs1);
|
||||||
name_all_type_vars(variable2, &mut subs2);
|
name_all_type_vars(variable2, &mut subs2);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue