From cd09467a0943ea0c98b516a3c111b0186f6a0a4f Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 16:48:04 -0500 Subject: [PATCH 1/8] Remove stray dbg --- src/parse/number_literal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse/number_literal.rs b/src/parse/number_literal.rs index 4130e838ec..f0e542309d 100644 --- a/src/parse/number_literal.rs +++ b/src/parse/number_literal.rs @@ -139,7 +139,7 @@ where let next_state = state.advance_without_indenting(bytes_parsed)?; - Ok((dbg!(expr), next_state)) + Ok((expr, next_state)) } #[derive(Debug, PartialEq, Eq)] From e831e165ddbcf57b5d1509bbca4690d44c1f1f23 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 13:36:06 -0500 Subject: [PATCH 2/8] Add basic support for Record and Alias --- src/can/expr.rs | 34 +++++++++++++++++++-- src/can/mod.rs | 1 + src/pretty_print_types.rs | 16 ++++++++++ src/solve.rs | 28 ++++++++++++++++++ src/subs.rs | 4 +++ src/types.rs | 11 +++++++ src/unify.rs | 62 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 153 insertions(+), 3 deletions(-) diff --git a/src/can/expr.rs b/src/can/expr.rs index 219899c468..5e14d17258 100644 --- a/src/can/expr.rs +++ b/src/can/expr.rs @@ -1,5 +1,6 @@ use crate::can::def::{can_defs_with_return, Def, Info}; use crate::can::env::Env; +use crate::can::ident::Lowercase; use crate::can::num::{ finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result, int_expr_from_result, @@ -637,8 +638,37 @@ pub fn canonicalize_expr( (expr, output, And(constraints)) } - ast::Expr::Access(_, _) - | ast::Expr::AccessorFunction(_) + ast::Expr::Access(record_expr, field) => { + 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::GlobalTag(_) | ast::Expr::PrivateTag(_) diff --git a/src/can/mod.rs b/src/can/mod.rs index 5f92ccfa3d..c26f7d00a9 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -1,6 +1,7 @@ pub mod def; pub mod env; pub mod expr; +pub mod ident; pub mod module; pub mod num; pub mod operator; diff --git a/src/pretty_print_types.rs b/src/pretty_print_types.rs index b74fee553c..635f5323b2 100644 --- a/src/pretty_print_types.rs +++ b/src/pretty_print_types.rs @@ -89,11 +89,19 @@ fn find_names_needed( find_names_needed(ret_var, subs, roots, root_appearances, names_taken); } + Structure(Record(_, _)) => { + panic!("TODO find_names_needed Record"); + let x = 5; + } RigidVar(name) => { // User-defined names are already taken. // We must not accidentally generate names that collide with them! names_taken.insert(name.to_string()); } + Alias(_, _, _, _) => { + panic!("TODO find_names_needed Alias"); + let x = 5; + } Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => { // Errors and empty records don't need names. } @@ -173,6 +181,10 @@ fn write_content(content: Content, subs: &mut Subs, buf: &mut String, parens: Pa FlexVar(None) => buf.push_str(WILDCARD), RigidVar(name) => buf.push_str(&name), Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens), + Alias(_, _, _, _) => { + panic!("TODO write_content Alias"); + let x = 5; + } Error(_) => buf.push_str(""), } } @@ -195,6 +207,10 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, paren ), EmptyRecord => buf.push_str(EMPTY_RECORD), Func(args, ret) => write_fn(args, ret, subs, buf, parens), + Record(_, _) => { + panic!("TODO write_flat_type Record"); + let x = 5; + } Erroneous(problem) => { buf.push_str(&format!("", problem)); } diff --git a/src/solve.rs b/src/solve.rs index 048287d483..207e7f3cd0 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -122,6 +122,34 @@ fn type_to_variable(subs: &mut Subs, aliases: &ImMap, Variable>, typ: T 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.clone())); + 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) => { let content = Content::Structure(FlatType::Erroneous(problem)); diff --git a/src/subs.rs b/src/subs.rs index 62592e48fa..fa89b18cff 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -1,3 +1,5 @@ +use crate::can::ident::{Lowercase, ModuleName, Uppercase}; +use crate::collections::ImMap; use crate::ena::unify::{InPlace, UnificationTable, UnifyKey}; use crate::types::Problem; use std::fmt; @@ -206,6 +208,7 @@ pub enum Content { /// name given in a user-written annotation RigidVar(Box), Structure(FlatType), + Alias(ModuleName, Uppercase, Vec<(Lowercase, Variable)>, Variable), Error(Problem), } @@ -217,6 +220,7 @@ pub enum FlatType { args: Vec, }, Func(Vec, Variable), + Record(ImMap, Variable), Erroneous(Problem), EmptyRecord, } diff --git a/src/types.rs b/src/types.rs index 246f5c23a1..fbf0fafe9c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,4 @@ +use crate::can::ident::{Lowercase, ModuleName, Uppercase}; use crate::can::symbol::Symbol; use crate::collections::SendMap; use crate::operator::{ArgSide, BinOp}; @@ -26,6 +27,8 @@ pub enum Type { EmptyRec, /// A function. The types of its arguments, then the type of its return value. Function(Vec, Box), + Record(SendMap, Box), + Alias(ModuleName, Uppercase, Vec<(Lowercase, Type)>, Box), /// Applying a type to some arguments (e.g. Map.Map String Int) Apply { module_name: Box, @@ -82,6 +85,14 @@ impl fmt::Debug for Type { write!(f, ")") } + Type::Alias(_, _, _, _) => { + panic!("TODO fmt type aliases"); + let x = 5; + } + Type::Record(_, _) => { + panic!("TODO fmt record types"); + let x = 5; + } } } } diff --git a/src/unify.rs b/src/unify.rs index 1281665b68..b483bd279c 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -1,3 +1,5 @@ +use crate::can::ident::Lowercase; +use crate::collections::ImMap; use crate::subs::Content::{self, *}; use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable}; use crate::types::Problem; @@ -9,6 +11,11 @@ struct Context { second_desc: Descriptor, } +struct RecordStructure { + fields: ImMap, + extension: Variable, +} + #[inline(always)] pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { if !subs.equivalent(var1, var2) { @@ -28,6 +35,10 @@ fn unify_context(subs: &mut Subs, ctx: &Context) { FlexVar(ref opt_name) => unify_flex(subs, ctx, opt_name, &ctx.second_desc.content), RigidVar(ref name) => unify_rigid(subs, ctx, name, &ctx.second_desc.content), Structure(ref flat_type) => unify_structure(subs, ctx, flat_type, &ctx.second_desc.content), + Alias(_, _, _, _) => { + panic!("TODO unify Alias"); + let x = 5; + } Error(ref problem) => { // Error propagates. Whatever we're comparing it to doesn't matter! merge(subs, ctx, Error(problem.clone())) @@ -40,6 +51,7 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: match other { FlexVar(_) => { // If the other is flex, Structure wins! + // merge(subs, ctx, Structure(flat_type.clone())) } RigidVar(_) => { @@ -50,6 +62,10 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: // Unify the two flat types unify_flat_type(subs, ctx, flat_type, other_flat_type) } + Alias(_, _, _, _) => { + panic!("TODO unify_structure Alias"); + let x = 5; + } Error(problem) => { // Error propagates. merge(subs, ctx, Error(problem.clone())) @@ -57,12 +73,28 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: } } +fn unify_record(ctx: &Context, structure1: RecordStructure, structure2: RecordStructure) { + panic!("TODO unify_record"); + let x = 5; +} + #[inline(always)] fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) { use crate::subs::FlatType::*; match (left, right) { (EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())), + + (Record(fields, ext), EmptyRecord) if fields.is_empty() => unify(subs, *ext, ctx.second), + + (EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext), + + (Record(fields1, ext1), Record(fields2, ext2)) => { + let structure1 = gather_fields(subs, fields1.clone(), *ext1); + let structure2 = gather_fields(subs, fields2.clone(), *ext2); + + unify_record(ctx, structure1, structure2) + } ( Apply { module_name: l_module_name, @@ -124,6 +156,10 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) { // rigid names are the same. merge(subs, ctx, Error(Problem::GenericMismatch)) } + Alias(_, _, _, _) => { + panic!("TODO unify_rigid Alias"); + panic!("TODO"); + } Error(problem) => { // Error propagates. merge(subs, ctx, Error(problem.clone())) @@ -138,7 +174,7 @@ fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option>, other // If both are flex, and only left has a name, keep the name around. merge(subs, ctx, FlexVar(opt_name.clone())) } - FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Error(_) => { + FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) | Error(_) => { // In all other cases, if left is flex, defer to right. // (This includes using right's name if both are flex and named.) merge(subs, ctx, other.clone()) @@ -146,6 +182,30 @@ fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option>, other } } +fn gather_fields( + subs: &mut Subs, + fields: ImMap, + var: Variable, +) -> RecordStructure { + use crate::subs::FlatType::*; + + match subs.get(var).content { + Structure(Record(sub_fields, sub_ext)) => { + gather_fields(subs, fields.union(sub_fields), sub_ext) + } + + Alias(_, _, _, var) => { + // TODO according to elm/compiler: "TODO may be dropping useful alias info here" + gather_fields(subs, fields, var) + } + + _ => RecordStructure { + fields, + extension: var, + }, + } +} + fn merge(subs: &mut Subs, ctx: &Context, content: Content) { let rank = ctx.first_desc.rank.min(ctx.second_desc.rank); let desc = Descriptor { From 0093298ed09c50ab616bce9def5404dc531effe1 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 18:35:06 -0500 Subject: [PATCH 3/8] Get more of unification compiling --- src/unify.rs | 72 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/src/unify.rs b/src/unify.rs index b483bd279c..9de032c497 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -1,4 +1,4 @@ -use crate::can::ident::Lowercase; +use crate::can::ident::{Lowercase, ModuleName, Uppercase}; use crate::collections::ImMap; use crate::subs::Content::{self, *}; use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable}; @@ -26,32 +26,71 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { second_desc: subs.get(var2), }; - unify_context(subs, &ctx) + unify_context(subs, ctx) } } -fn unify_context(subs: &mut Subs, ctx: &Context) { - match ctx.first_desc.content { - FlexVar(ref opt_name) => unify_flex(subs, ctx, opt_name, &ctx.second_desc.content), - RigidVar(ref name) => unify_rigid(subs, ctx, name, &ctx.second_desc.content), - Structure(ref flat_type) => unify_structure(subs, ctx, flat_type, &ctx.second_desc.content), - Alias(_, _, _, _) => { - panic!("TODO unify Alias"); - let x = 5; - } - Error(ref problem) => { +fn unify_context(subs: &mut Subs, ctx: Context) { + match &ctx.first_desc.content { + FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content), + RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content), + Structure(flat_type) => unify_structure(subs, &ctx, flat_type, &ctx.second_desc.content), + Alias(home, name, args, real_var) => unify_alias(subs, &ctx, home, name, args, real_var), + Error(problem) => { // Error propagates. Whatever we're comparing it to doesn't matter! - merge(subs, ctx, Error(problem.clone())) + merge(subs, &ctx, Error(problem.clone())) } } } +#[inline(always)] +fn unify_alias( + subs: &mut Subs, + ctx: &Context, + home: &ModuleName, + name: &Uppercase, + args: &Vec<(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.clone(), *real_var), + ) + } + RigidVar(_) => unify(subs, *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, *l_var, *r_var); + } + + merge(subs, &ctx, other_content.clone()) + } else if args.len() > other_args.len() { + merge(subs, &ctx, Error(Problem::ExtraArguments)) + } else { + merge(subs, &ctx, Error(Problem::MissingArguments)) + } + } else { + unify(subs, *real_var, *other_real_var) + } + } + Structure(_) => unify(subs, *real_var, ctx.second), + Error(problem) => merge(subs, ctx, Error(problem.clone())), + } +} + #[inline(always)] fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) { match other { FlexVar(_) => { // If the other is flex, Structure wins! - // merge(subs, ctx, Structure(flat_type.clone())) } RigidVar(_) => { @@ -62,10 +101,7 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: // Unify the two flat types unify_flat_type(subs, ctx, flat_type, other_flat_type) } - Alias(_, _, _, _) => { - panic!("TODO unify_structure Alias"); - let x = 5; - } + Alias(_, _, _, real_var) => unify(subs, ctx.first, *real_var), Error(problem) => { // Error propagates. merge(subs, ctx, Error(problem.clone())) From e949c56f7030182551019255fd2d2414f41bb77d Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 19:34:06 -0500 Subject: [PATCH 4/8] Try out UnifyResult --- src/unify.rs | 238 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 195 insertions(+), 43 deletions(-) diff --git a/src/unify.rs b/src/unify.rs index 9de032c497..cc169d50d4 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -13,12 +13,18 @@ struct Context { struct RecordStructure { fields: ImMap, - extension: Variable, + ext: Variable, } +type UnifyResult = Result<(), Problem>; + +type Problems = Vec; + #[inline(always)] -pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { - if !subs.equivalent(var1, var2) { +pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> UnifyResult { + if subs.equivalent(var1, var2) { + Ok(()) + } else { let ctx = Context { first: var1, first_desc: subs.get(var1), @@ -30,7 +36,7 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { } } -fn unify_context(subs: &mut Subs, ctx: Context) { +fn unify_context(subs: &mut Subs, ctx: Context) -> UnifyResult { match &ctx.first_desc.content { FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content), RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content), @@ -38,7 +44,9 @@ fn unify_context(subs: &mut Subs, ctx: Context) { Alias(home, name, args, real_var) => unify_alias(subs, &ctx, home, name, args, real_var), Error(problem) => { // Error propagates. Whatever we're comparing it to doesn't matter! - merge(subs, &ctx, Error(problem.clone())) + merge(subs, &ctx, Error(problem.clone())); + + Err(problem.clone()) } } } @@ -51,7 +59,7 @@ fn unify_alias( name: &Uppercase, args: &Vec<(Lowercase, Variable)>, real_var: &Variable, -) { +) -> UnifyResult { let other_content = &ctx.second_desc.content; match other_content { @@ -61,41 +69,71 @@ fn unify_alias( subs, &ctx, Alias(home.clone(), name.clone(), args.clone(), *real_var), - ) + ); + + Ok(()) } RigidVar(_) => unify(subs, *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() { + let mut answer = Ok(()); + for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) { - unify(subs, *l_var, *r_var); + let result = unify(subs, *l_var, *r_var); + + answer = answer.and_then(|()| result); } - merge(subs, &ctx, other_content.clone()) + merge(subs, &ctx, other_content.clone()); + + answer } else if args.len() > other_args.len() { - merge(subs, &ctx, Error(Problem::ExtraArguments)) + let problem = Problem::ExtraArguments; + + merge(subs, &ctx, Error(problem.clone())); + + Err(problem) } else { - merge(subs, &ctx, Error(Problem::MissingArguments)) + let problem = Problem::MissingArguments; + + merge(subs, &ctx, Error(problem.clone())); + + Err(problem) } } else { unify(subs, *real_var, *other_real_var) } } Structure(_) => unify(subs, *real_var, ctx.second), - Error(problem) => merge(subs, ctx, Error(problem.clone())), + Error(problem) => { + merge(subs, ctx, Error(problem.clone())); + + Err(problem.clone()) + } } } #[inline(always)] -fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) { +fn unify_structure( + subs: &mut Subs, + ctx: &Context, + flat_type: &FlatType, + other: &Content, +) -> UnifyResult { match other { FlexVar(_) => { // If the other is flex, Structure wins! - merge(subs, ctx, Structure(flat_type.clone())) + merge(subs, ctx, Structure(flat_type.clone())); + + Ok(()) } RigidVar(_) => { + let problem = Problem::GenericMismatch; // Type mismatch! Rigid can only unify with flex. - merge(subs, ctx, Error(Problem::GenericMismatch)) + merge(subs, ctx, Error(problem.clone())); + + Err(problem) } Structure(ref other_flat_type) => { // Unify the two flat types @@ -104,32 +142,114 @@ fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: Alias(_, _, _, real_var) => unify(subs, ctx.first, *real_var), Error(problem) => { // Error propagates. - merge(subs, ctx, Error(problem.clone())) + merge(subs, ctx, Error(problem.clone())); + + Err(problem.clone()) } } } -fn unify_record(ctx: &Context, structure1: RecordStructure, structure2: RecordStructure) { - panic!("TODO unify_record"); - let x = 5; +fn unify_record( + subs: &mut Subs, + ctx: &Context, + rec1: RecordStructure, + rec2: RecordStructure, +) -> UnifyResult { + 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, rec1.ext, rec2.ext); + unify_shared_fields(subs, ctx, shared_fields, ImMap::default(), rec1.ext) + } else { + // subRecord <- fresh context (Structure (Record1 uniqueFields2 ext2)) + // subUnify ext1 subRecord + // unifySharedFields context sharedFields Map.empty subRecord + panic!("TODO 1"); + } + } else if unique_fields2.is_empty() { + // subRecord <- fresh context (Structure (Record1 uniqueFields1 ext1)) + // subUnify subRecord ext2 + // unifySharedFields context sharedFields Map.empty subRecord + panic!("TODO 2"); + } else { + // let otherFields = Map.union uniqueFields1 uniqueFields2 + // ext <- fresh context Type.unnamedFlexVar + // sub1 <- fresh context (Structure (Record1 uniqueFields1 ext)) + // sub2 <- fresh context (Structure (Record1 uniqueFields2 ext)) + // subUnify ext1 sub2 + // subUnify sub1 ext2 + // unifySharedFields context sharedFields otherFields ext + // + panic!("TODO 3"); + } +} + +fn unify_shared_fields( + subs: &mut Subs, + ctx: &Context, + shared_fields: ImMap, + other_fields: ImMap, + ext: Variable, +) -> UnifyResult { + let mut matching_fields = ImMap::default(); + let num_shared_fields = shared_fields.len(); + + for (name, (actual, expected)) in shared_fields { + // 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. + if unify(subs, actual, expected).is_ok() { + 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)); + + Ok(()) + } else { + let problem = Problem::GenericMismatch; + + // Type mismatch! Rigid can only unify with flex. + merge(subs, ctx, Error(problem.clone())); + + Err(problem) + } } #[inline(always)] -fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) { +fn unify_flat_type( + subs: &mut Subs, + ctx: &Context, + left: &FlatType, + right: &FlatType, +) -> UnifyResult { use crate::subs::FlatType::*; match (left, right) { - (EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())), + (EmptyRecord, EmptyRecord) => { + merge(subs, ctx, Structure(left.clone())); + + Ok(()) + } (Record(fields, ext), EmptyRecord) if fields.is_empty() => unify(subs, *ext, ctx.second), (EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext), (Record(fields1, ext1), Record(fields2, ext2)) => { - let structure1 = gather_fields(subs, fields1.clone(), *ext1); - let structure2 = gather_fields(subs, fields2.clone(), *ext2); + let rec1 = gather_fields(subs, fields1.clone(), *ext1); + let rec2 = gather_fields(subs, fields2.clone(), *ext2); - unify_record(ctx, structure1, structure2) + unify_record(subs, ctx, rec1, rec2) } ( Apply { @@ -153,21 +273,35 @@ fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &Flat name: (*r_type_name).clone(), args: (*r_args).clone(), }), - ) + ); + + Ok(()) } (Func(l_args, l_ret), Func(r_args, r_ret)) => { if l_args.len() == r_args.len() { unify_zip(subs, l_args.iter(), r_args.iter()); - unify(subs, *l_ret, *r_ret); + let answer = unify(subs, *l_ret, *r_ret); - merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret))) + merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret))); + + answer } else if l_args.len() > r_args.len() { - merge(subs, ctx, Error(Problem::ExtraArguments)) + merge(subs, ctx, Error(Problem::ExtraArguments)); + + Ok(()) } else { - merge(subs, ctx, Error(Problem::MissingArguments)) + merge(subs, ctx, Error(Problem::MissingArguments)); + + Ok(()) } } - _ => merge(subs, ctx, Error(Problem::GenericMismatch)), + _ => { + let problem = Problem::GenericMismatch; + + merge(subs, ctx, Error(problem.clone())); + + Err(problem) + } } } @@ -181,39 +315,60 @@ where } #[inline(always)] -fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) { +fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) -> UnifyResult { match other { FlexVar(_) => { // If the other is flex, rigid wins! - merge(subs, ctx, RigidVar(name.into())) + merge(subs, ctx, RigidVar(name.into())); + + Ok(()) } RigidVar(_) | Structure(_) => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. - merge(subs, ctx, Error(Problem::GenericMismatch)) + merge(subs, ctx, Error(Problem::GenericMismatch)); + + Ok(()) } Alias(_, _, _, _) => { panic!("TODO unify_rigid Alias"); - panic!("TODO"); + + Ok(()) } Error(problem) => { // Error propagates. - merge(subs, ctx, Error(problem.clone())) + merge(subs, ctx, Error(problem.clone())); + + Err(problem.clone()) } } } #[inline(always)] -fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option>, other: &Content) { +fn unify_flex( + subs: &mut Subs, + ctx: &Context, + opt_name: &Option>, + other: &Content, +) -> UnifyResult { match other { FlexVar(None) => { // 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())); + + Ok(()) } - FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) | Error(_) => { + FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) => { // In all other cases, if left is flex, defer to right. // (This includes using right's name if both are flex and named.) - merge(subs, ctx, other.clone()) + merge(subs, ctx, other.clone()); + + Ok(()) + } + Error(problem) => { + merge(subs, ctx, Error(problem.clone())); + + Err(problem.clone()) } } } @@ -235,10 +390,7 @@ fn gather_fields( gather_fields(subs, fields, var) } - _ => RecordStructure { - fields, - extension: var, - }, + _ => RecordStructure { fields, ext: var }, } } From ca219a1b643d7e8de14eb04f75397f163e201d79 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 19:43:31 -0500 Subject: [PATCH 5/8] Record problems in unification --- src/infer.rs | 10 +- src/load/mod.rs | 7 +- src/solve.rs | 23 +++-- src/unify.rs | 173 +++++++++++++++------------------ tests/test_eval.rs | 3 +- tests/test_infer.rs | 3 +- tests/test_load.rs | 3 +- tests/test_uniqueness_infer.rs | 5 +- 8 files changed, 114 insertions(+), 113 deletions(-) diff --git a/src/infer.rs b/src/infer.rs index 80d6998846..30757cc68e 100644 --- a/src/infer.rs +++ b/src/infer.rs @@ -3,11 +3,17 @@ use crate::collections::ImMap; use crate::solve::solve; use crate::subs::{Content, Subs, Variable}; 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 = ImMap::default(); - solve(&env, subs, constraint); + solve(&env, problems, subs, constraint); subs.get(expr_var).content } diff --git a/src/load/mod.rs b/src/load/mod.rs index b6770703da..f4b22f80bc 100644 --- a/src/load/mod.rs +++ b/src/load/mod.rs @@ -1,6 +1,7 @@ use crate::can::def::Def; use crate::can::module::{canonicalize_module_defs, Module}; use crate::can::scope::Scope; +use crate::unify::Problems; use crate::can::symbol::Symbol; use crate::collections::{ImMap, SendMap, SendSet}; use crate::ident::Ident; @@ -348,7 +349,7 @@ 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::*; let mut vars_by_symbol: ImMap = ImMap::default(); @@ -403,8 +404,8 @@ pub fn solve_loaded(module: &Module, subs: &mut Subs, loaded_deps: LoadedDeps) { } 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); } diff --git a/src/solve.rs b/src/solve.rs index 207e7f3cd0..9e956ec8c9 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -3,11 +3,16 @@ use crate::collections::ImMap; use crate::subs::{Content, Descriptor, FlatType, Subs, Variable}; use crate::types::Constraint::{self, *}; use crate::types::Type::{self, *}; -use crate::unify::unify; +use crate::unify::{unify, Problems}; type Env = ImMap; -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 { True => (), 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 expected = type_to_var(subs, expected_type.clone().get_type()); - unify(subs, actual, expected); + unify(subs, problems, actual, expected); } Lookup(symbol, expected_type, _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()); - unify(subs, actual, expected); + unify(subs, problems, actual, expected); } And(sub_constraints) => { 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) => { @@ -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 expected = type_to_var(subs, expected.clone().get_type()); - unify(subs, actual, expected); + unify(subs, problems, actual, expected); } Let(let_con) => { match &let_con.ret_constraint { True => { // If the return expression is guaranteed to solve, // 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 => { // 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. 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 // 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! } diff --git a/src/unify.rs b/src/unify.rs index cc169d50d4..952f8c5f84 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -16,15 +16,11 @@ struct RecordStructure { ext: Variable, } -type UnifyResult = Result<(), Problem>; - -type Problems = Vec; +pub type Problems = Vec; #[inline(always)] -pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> UnifyResult { - if subs.equivalent(var1, var2) { - Ok(()) - } else { +pub fn unify(subs: &mut Subs, problems: &mut Problems, var1: Variable, var2: Variable) { + if !subs.equivalent(var1, var2) { let ctx = Context { first: var1, first_desc: subs.get(var1), @@ -32,21 +28,24 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> UnifyResult { second_desc: subs.get(var2), }; - unify_context(subs, ctx) + unify_context(subs, problems, ctx) } } -fn unify_context(subs: &mut Subs, ctx: Context) -> UnifyResult { +fn unify_context(subs: &mut Subs, problems: &mut Problems, ctx: Context) { match &ctx.first_desc.content { - FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content), - RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content), - Structure(flat_type) => unify_structure(subs, &ctx, flat_type, &ctx.second_desc.content), - Alias(home, name, args, real_var) => unify_alias(subs, &ctx, home, name, args, real_var), + FlexVar(opt_name) => unify_flex(subs, problems, &ctx, opt_name, &ctx.second_desc.content), + RigidVar(name) => unify_rigid(subs, problems, &ctx, name, &ctx.second_desc.content), + Structure(flat_type) => { + 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! merge(subs, &ctx, Error(problem.clone())); - - Err(problem.clone()) + problems.push(problem.clone()); } } } @@ -54,12 +53,13 @@ fn unify_context(subs: &mut Subs, ctx: Context) -> UnifyResult { #[inline(always)] fn unify_alias( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, home: &ModuleName, name: &Uppercase, args: &Vec<(Lowercase, Variable)>, real_var: &Variable, -) -> UnifyResult { +) { let other_content = &ctx.second_desc.content; match other_content { @@ -70,46 +70,35 @@ fn unify_alias( &ctx, Alias(home.clone(), name.clone(), args.clone(), *real_var), ); - - Ok(()) } - RigidVar(_) => unify(subs, *real_var, ctx.second), + 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() { - let mut answer = Ok(()); - for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) { - let result = unify(subs, *l_var, *r_var); - - answer = answer.and_then(|()| result); + unify(subs, problems, *l_var, *r_var); } merge(subs, &ctx, other_content.clone()); - - answer } else if args.len() > other_args.len() { let problem = Problem::ExtraArguments; merge(subs, &ctx, Error(problem.clone())); - - Err(problem) + problems.push(problem.clone()); } else { let problem = Problem::MissingArguments; merge(subs, &ctx, Error(problem.clone())); - - Err(problem) + problems.push(problem.clone()); } } else { - unify(subs, *real_var, *other_real_var) + unify(subs, problems, *real_var, *other_real_var) } } - Structure(_) => unify(subs, *real_var, ctx.second), + Structure(_) => unify(subs, problems, *real_var, ctx.second), Error(problem) => { merge(subs, ctx, Error(problem.clone())); - - Err(problem.clone()) + problems.push(problem.clone()); } } } @@ -117,44 +106,42 @@ fn unify_alias( #[inline(always)] fn unify_structure( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, flat_type: &FlatType, other: &Content, -) -> UnifyResult { +) { match other { FlexVar(_) => { // If the other is flex, Structure wins! merge(subs, ctx, Structure(flat_type.clone())); - - Ok(()) } RigidVar(_) => { let problem = Problem::GenericMismatch; // Type mismatch! Rigid can only unify with flex. merge(subs, ctx, Error(problem.clone())); - - Err(problem) + problems.push(problem.clone()); } Structure(ref other_flat_type) => { // 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, ctx.first, *real_var), + Alias(_, _, _, real_var) => unify(subs, problems, ctx.first, *real_var), Error(problem) => { // Error propagates. merge(subs, ctx, Error(problem.clone())); - - Err(problem.clone()) + problems.push(problem.clone()); } } } fn unify_record( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, rec1: RecordStructure, rec2: RecordStructure, -) -> UnifyResult { +) { let fields1 = rec1.fields; let fields2 = rec2.fields; let shared_fields = fields1 @@ -165,8 +152,15 @@ fn unify_record( if unique_fields1.is_empty() { if unique_fields2.is_empty() { - unify(subs, rec1.ext, rec2.ext); - unify_shared_fields(subs, ctx, shared_fields, ImMap::default(), rec1.ext) + unify(subs, problems, rec1.ext, rec2.ext); + unify_shared_fields( + subs, + problems, + ctx, + shared_fields, + ImMap::default(), + rec1.ext, + ) } else { // subRecord <- fresh context (Structure (Record1 uniqueFields2 ext2)) // subUnify ext1 subRecord @@ -193,18 +187,23 @@ fn unify_record( fn unify_shared_fields( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, shared_fields: ImMap, other_fields: ImMap, ext: Variable, -) -> UnifyResult { +) { 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. - if unify(subs, actual, expected).is_ok() { + unify(subs, problems, actual, expected); + + if problems.len() == prev_problem_count { matching_fields.insert(name, actual); } } @@ -213,43 +212,43 @@ fn unify_shared_fields( let flat_type = FlatType::Record(matching_fields.union(other_fields), ext); merge(subs, ctx, Structure(flat_type)); - - Ok(()) } else { let problem = Problem::GenericMismatch; // Type mismatch! Rigid can only unify with flex. merge(subs, ctx, Error(problem.clone())); - - Err(problem) + problems.push(problem.clone()); } } #[inline(always)] fn unify_flat_type( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, left: &FlatType, right: &FlatType, -) -> UnifyResult { +) { use crate::subs::FlatType::*; match (left, right) { (EmptyRecord, EmptyRecord) => { merge(subs, ctx, Structure(left.clone())); - - Ok(()) } - (Record(fields, ext), EmptyRecord) if fields.is_empty() => unify(subs, *ext, ctx.second), + (Record(fields, ext), EmptyRecord) if fields.is_empty() => { + unify(subs, problems, *ext, ctx.second) + } - (EmptyRecord, Record(fields, ext)) if fields.is_empty() => unify(subs, ctx.first, *ext), + (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, fields1.clone(), *ext1); - let rec2 = gather_fields(subs, fields2.clone(), *ext2); + let rec1 = gather_fields(subs, problems, fields1.clone(), *ext1); + let rec2 = gather_fields(subs, problems, fields2.clone(), *ext2); - unify_record(subs, ctx, rec1, rec2) + unify_record(subs, problems, ctx, rec1, rec2) } ( Apply { @@ -263,7 +262,7 @@ fn unify_flat_type( args: r_args, }, ) 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( subs, @@ -274,72 +273,61 @@ fn unify_flat_type( args: (*r_args).clone(), }), ); - - Ok(()) } (Func(l_args, l_ret), Func(r_args, r_ret)) => { if l_args.len() == r_args.len() { - unify_zip(subs, l_args.iter(), r_args.iter()); - let answer = unify(subs, *l_ret, *r_ret); - + unify_zip(subs, problems, l_args.iter(), r_args.iter()); + unify(subs, problems, *l_ret, *r_ret); merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret))); - - answer } else if l_args.len() > r_args.len() { merge(subs, ctx, Error(Problem::ExtraArguments)); - - Ok(()) } else { merge(subs, ctx, Error(Problem::MissingArguments)); - - Ok(()) } } _ => { let problem = Problem::GenericMismatch; merge(subs, ctx, Error(problem.clone())); - - Err(problem) + 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 I: Iterator, { 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)] -fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) -> UnifyResult { +fn unify_rigid( + subs: &mut Subs, + problems: &mut Problems, + ctx: &Context, + name: &str, + other: &Content, +) { match other { FlexVar(_) => { // If the other is flex, rigid wins! merge(subs, ctx, RigidVar(name.into())); - - Ok(()) } RigidVar(_) | Structure(_) => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. merge(subs, ctx, Error(Problem::GenericMismatch)); - - Ok(()) } Alias(_, _, _, _) => { panic!("TODO unify_rigid Alias"); - - Ok(()) } Error(problem) => { // Error propagates. merge(subs, ctx, Error(problem.clone())); - - Err(problem.clone()) + problems.push(problem.clone()); } } } @@ -347,34 +335,31 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) -> U #[inline(always)] fn unify_flex( subs: &mut Subs, + problems: &mut Problems, ctx: &Context, opt_name: &Option>, other: &Content, -) -> UnifyResult { +) { match other { FlexVar(None) => { // If both are flex, and only left has a name, keep the name around. merge(subs, ctx, FlexVar(opt_name.clone())); - - Ok(()) } FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Alias(_, _, _, _) => { // In all other cases, if left is flex, defer to right. // (This includes using right's name if both are flex and named.) merge(subs, ctx, other.clone()); - - Ok(()) } Error(problem) => { merge(subs, ctx, Error(problem.clone())); - - Err(problem.clone()) + problems.push(problem.clone()); } } } fn gather_fields( subs: &mut Subs, + problems: &mut Problems, fields: ImMap, var: Variable, ) -> RecordStructure { @@ -382,12 +367,12 @@ fn gather_fields( match subs.get(var).content { Structure(Record(sub_fields, sub_ext)) => { - gather_fields(subs, fields.union(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, fields, var) + gather_fields(subs, problems, fields, var) } _ => RecordStructure { fields, ext: var }, diff --git a/tests/test_eval.rs b/tests/test_eval.rs index 25467f5ffa..2e8f32e5da 100644 --- a/tests/test_eval.rs +++ b/tests/test_eval.rs @@ -25,7 +25,8 @@ mod test_gen { ($src:expr, $expected:expr, $ty:ty) => { let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src); 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 builder = context.create_builder(); diff --git a/tests/test_infer.rs b/tests/test_infer.rs index 63a27e752e..b1877d9cee 100644 --- a/tests/test_infer.rs +++ b/tests/test_infer.rs @@ -20,7 +20,8 @@ mod test_infer { fn infer_eq(src: &str, expected: &str) { let (_, _output, _, var_store, variable, constraint) = can_expr(src); 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); diff --git a/tests/test_load.rs b/tests/test_load.rs index 277af9458e..b51bb59f48 100644 --- a/tests/test_load.rs +++ b/tests/test_load.rs @@ -154,7 +154,8 @@ mod test_load { 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! { "WithBuiltins.floatTest" => "Float", diff --git a/tests/test_uniqueness_infer.rs b/tests/test_uniqueness_infer.rs index e99348f4ed..29c47bf40b 100644 --- a/tests/test_uniqueness_infer.rs +++ b/tests/test_uniqueness_infer.rs @@ -33,8 +33,9 @@ mod test_infer_uniq { let mut subs1 = Subs::new(var_store1.into()); let mut subs2 = Subs::new(var_store2.into()); - let content1 = infer_expr(&mut subs1, &constraint1, variable1); - let content2 = infer_expr(&mut subs2, &constraint2, variable2); + let mut unify_problems = Vec::new(); + 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(variable2, &mut subs2); From f2f305b7a89039331e9779813144232bb0d9ca86 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 23:02:08 -0500 Subject: [PATCH 6/8] Clean up warnings --- src/pretty_print_types.rs | 4 --- src/subs.rs | 4 +++ src/types.rs | 2 -- src/unify.rs | 54 +++++++++++++++++++++++++++------------ 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/pretty_print_types.rs b/src/pretty_print_types.rs index 635f5323b2..d285ca64f3 100644 --- a/src/pretty_print_types.rs +++ b/src/pretty_print_types.rs @@ -91,7 +91,6 @@ fn find_names_needed( } Structure(Record(_, _)) => { panic!("TODO find_names_needed Record"); - let x = 5; } RigidVar(name) => { // User-defined names are already taken. @@ -100,7 +99,6 @@ fn find_names_needed( } Alias(_, _, _, _) => { panic!("TODO find_names_needed Alias"); - let x = 5; } Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => { // Errors and empty records don't need names. @@ -183,7 +181,6 @@ fn write_content(content: Content, subs: &mut Subs, buf: &mut String, parens: Pa Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens), Alias(_, _, _, _) => { panic!("TODO write_content Alias"); - let x = 5; } Error(_) => buf.push_str(""), } @@ -209,7 +206,6 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, paren Func(args, ret) => write_fn(args, ret, subs, buf, parens), Record(_, _) => { panic!("TODO write_flat_type Record"); - let x = 5; } Erroneous(problem) => { buf.push_str(&format!("", problem)); diff --git a/src/subs.rs b/src/subs.rs index fa89b18cff..97cfef067f 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -130,6 +130,10 @@ impl Subs { 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. pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) { let l_root = self.utable.get_root_key(left); diff --git a/src/types.rs b/src/types.rs index fbf0fafe9c..9bf9fb394b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -87,11 +87,9 @@ impl fmt::Debug for Type { } Type::Alias(_, _, _, _) => { panic!("TODO fmt type aliases"); - let x = 5; } Type::Record(_, _) => { panic!("TODO fmt record types"); - let x = 5; } } } diff --git a/src/unify.rs b/src/unify.rs index 952f8c5f84..1d2902deeb 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -162,26 +162,46 @@ fn unify_record( rec1.ext, ) } else { - // subRecord <- fresh context (Structure (Record1 uniqueFields2 ext2)) - // subUnify ext1 subRecord - // unifySharedFields context sharedFields Map.empty subRecord - panic!("TODO 1"); + 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() { - // subRecord <- fresh context (Structure (Record1 uniqueFields1 ext1)) - // subUnify subRecord ext2 - // unifySharedFields context sharedFields Map.empty subRecord - panic!("TODO 2"); + 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 otherFields = Map.union uniqueFields1 uniqueFields2 - // ext <- fresh context Type.unnamedFlexVar - // sub1 <- fresh context (Structure (Record1 uniqueFields1 ext)) - // sub2 <- fresh context (Structure (Record1 uniqueFields2 ext)) - // subUnify ext1 sub2 - // subUnify sub1 ext2 - // unifySharedFields context sharedFields otherFields ext - // - panic!("TODO 3"); + 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); } } From 99b14aed27a21701dc825755d6206264edd292af Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 23:03:49 -0500 Subject: [PATCH 7/8] Follow clippy's suggestions --- src/solve.rs | 2 +- src/unify.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/solve.rs b/src/solve.rs index 9e956ec8c9..c8d5259716 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -146,7 +146,7 @@ fn type_to_variable(subs: &mut Subs, aliases: &ImMap, Variable>, typ: T for (arg, arg_type) in args { let arg_var = type_to_variable(subs, aliases, arg_type.clone()); - arg_vars.push((arg.clone(), arg_var.clone())); + arg_vars.push((arg.clone(), arg_var)); new_aliases.insert(arg.into(), arg_var); } diff --git a/src/unify.rs b/src/unify.rs index 1d2902deeb..c8ed563716 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -40,7 +40,7 @@ fn unify_context(subs: &mut Subs, problems: &mut Problems, ctx: Context) { 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) + unify_alias(subs, problems, &ctx, home, name, args, *real_var) } Error(problem) => { // Error propagates. Whatever we're comparing it to doesn't matter! @@ -57,8 +57,8 @@ fn unify_alias( ctx: &Context, home: &ModuleName, name: &Uppercase, - args: &Vec<(Lowercase, Variable)>, - real_var: &Variable, + args: &[(Lowercase, Variable)], + real_var: Variable, ) { let other_content = &ctx.second_desc.content; @@ -68,10 +68,10 @@ fn unify_alias( merge( subs, &ctx, - Alias(home.clone(), name.clone(), args.clone(), *real_var), + Alias(home.clone(), name.clone(), args.to_owned(), real_var), ); } - RigidVar(_) => unify(subs, problems, *real_var, ctx.second), + 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() { @@ -92,10 +92,10 @@ fn unify_alias( problems.push(problem.clone()); } } else { - unify(subs, problems, *real_var, *other_real_var) + unify(subs, problems, real_var, *other_real_var) } } - Structure(_) => unify(subs, problems, *real_var, ctx.second), + Structure(_) => unify(subs, problems, real_var, ctx.second), Error(problem) => { merge(subs, ctx, Error(problem.clone())); problems.push(problem.clone()); From 7beca9e0faa95a4ad3facb657ec8aabfe9f53904 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 7 Dec 2019 23:08:36 -0500 Subject: [PATCH 8/8] cargo fmt --- src/load/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/load/mod.rs b/src/load/mod.rs index f4b22f80bc..be87f8623a 100644 --- a/src/load/mod.rs +++ b/src/load/mod.rs @@ -1,7 +1,6 @@ use crate::can::def::Def; use crate::can::module::{canonicalize_module_defs, Module}; use crate::can::scope::Scope; -use crate::unify::Problems; use crate::can::symbol::Symbol; use crate::collections::{ImMap, SendMap, SendSet}; use crate::ident::Ident; @@ -14,6 +13,7 @@ use crate::solve::solve; use crate::subs::VarStore; use crate::subs::{Subs, Variable}; use crate::types::Constraint; +use crate::unify::Problems; use bumpalo::Bump; use futures::future::join_all; use std::io; @@ -349,7 +349,12 @@ fn expose( } } -pub fn solve_loaded(module: &Module, problems: &mut Problems, subs: &mut Subs, loaded_deps: LoadedDeps) { +pub fn solve_loaded( + module: &Module, + problems: &mut Problems, + subs: &mut Subs, + loaded_deps: LoadedDeps, +) { use LoadedModule::*; let mut vars_by_symbol: ImMap = ImMap::default();