diff --git a/src/can/mod.rs b/src/can/mod.rs index e4f408ded8..1f164f3b16 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -19,7 +19,7 @@ use crate::graph::{strongly_connected_component, topological_sort}; use crate::ident::Ident; use crate::parse::ast::{self, Def}; use crate::region::{Located, Region}; -use crate::subs::{Subs, Variable}; +use crate::subs::{VarStore, Variable}; use crate::types::AnnotationSource::*; use crate::types::Constraint::{self, *}; use crate::types::Expected::{self, *}; @@ -48,7 +48,7 @@ type Rigids = ImMap, Type>; #[allow(clippy::too_many_arguments)] pub fn canonicalize_declaration<'a>( arena: &Bump, - subs: &mut Subs, + var_store: &VarStore, home: Box, name: Box, region: Region, @@ -74,7 +74,7 @@ pub fn canonicalize_declaration<'a>( let (loc_expr, output) = canonicalize_expr( &ImMap::default(), &mut env, - subs, + var_store, &mut scope, region, &loc_expr.value, @@ -104,7 +104,7 @@ impl Output { fn canonicalize_expr( rigids: &Rigids, env: &mut Env, - subs: &mut Subs, + var_store: &VarStore, scope: &mut Scope, region: Region, expr: &ast::Expr, @@ -115,13 +115,18 @@ fn canonicalize_expr( let (expr, output) = match expr { ast::Expr::Int(string) => { let (constraint, answer) = - int_expr_from_result(subs, finish_parsing_int(string), env, expected, region); + int_expr_from_result(var_store, finish_parsing_int(string), env, expected, region); (answer, Output::new(constraint)) } ast::Expr::Float(string) => { - let (constraint, answer) = - float_expr_from_result(subs, finish_parsing_float(string), env, expected, region); + let (constraint, answer) = float_expr_from_result( + var_store, + finish_parsing_float(string), + env, + expected, + region, + ); (answer, Output::new(constraint)) } @@ -147,19 +152,19 @@ fn canonicalize_expr( } ast::Expr::List(loc_elems) => { if loc_elems.is_empty() { - let list_var = subs.mk_flex_var(); + let list_var = var_store.fresh(); let constraint = Eq(constrain::empty_list_type(list_var), expected, region); (List(list_var, Vec::new()), Output::new(constraint)) } else { let mut can_elems = Vec::with_capacity(loc_elems.len()); - let list_var = subs.mk_flex_var(); // `v` in the type (List v) + let list_var = var_store.fresh(); // `v` in the type (List v) let list_type = Type::Variable(list_var); let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2)); let mut references = References::new(); for loc_elem in loc_elems.iter() { - let elem_var = subs.mk_flex_var(); + let elem_var = var_store.fresh(); let elem_type = Variable(elem_var); let elem_expected = NoExpectation(elem_type.clone()); let list_elem_constraint = Eq( @@ -170,7 +175,7 @@ fn canonicalize_expr( let (can_expr, elem_out) = canonicalize_expr( rigids, env, - subs, + var_store, scope, loc_elem.region, &loc_elem.value, @@ -229,7 +234,7 @@ fn canonicalize_expr( ast::Expr::Apply(loc_fn, loc_args, application_style) => { // The expression that evaluates to the function being called, e.g. `foo` in // (foo) bar baz - let fn_var = subs.mk_flex_var(); + let fn_var = var_store.fresh(); let fn_type = Variable(fn_var); let fn_region = loc_fn.region; let fn_expected = NoExpectation(fn_type.clone()); @@ -242,7 +247,7 @@ fn canonicalize_expr( let (fn_expr, mut output) = canonicalize_expr( rigids, env, - subs, + var_store, scope, loc_fn.region, &loc_fn.value, @@ -250,7 +255,7 @@ fn canonicalize_expr( ); // The function's return type - let ret_var = subs.mk_flex_var(); + let ret_var = var_store.fresh(); let ret_type = Variable(ret_var); // This will be used in the occurs check @@ -267,7 +272,7 @@ fn canonicalize_expr( for (index, loc_arg) in loc_args.iter().enumerate() { let region = loc_arg.region; - let arg_var = subs.mk_flex_var(); + let arg_var = var_store.fresh(); let arg_type = Variable(arg_var); // TODO look up the name and use NamedFnArg if possible. let reason = Reason::AnonymousFnArg { @@ -277,7 +282,7 @@ fn canonicalize_expr( let (arg_expr, arg_out) = canonicalize_expr( rigids, env, - subs, + var_store, scope, loc_arg.region, &loc_arg.value, @@ -354,7 +359,7 @@ fn canonicalize_expr( let mut output = Output::new(Lookup(symbol, expected, region)); let ident = Ident::new(module_parts, name); let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) { - Ok(symbol) => Var(subs.mk_flex_var(), symbol), + Ok(symbol) => Var(var_store.fresh(), symbol), Err(ident) => { let loc_ident = Located { region, @@ -454,7 +459,15 @@ fn canonicalize_expr( ast::Expr::Defs(defs, loc_ret) => { // The body expression gets a new scope for canonicalization, // so clone it. - can_defs(rigids, env, subs, scope.clone(), defs, expected, loc_ret) + can_defs( + rigids, + env, + var_store, + scope.clone(), + defs, + expected, + loc_ret, + ) } ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => { // The globally unique symbol that will refer to this closure once it gets converted @@ -495,7 +508,7 @@ fn canonicalize_expr( let mut shadowable_idents = scope.idents.clone(); remove_idents(&loc_pattern.value, &mut shadowable_idents); - let pattern_var = subs.mk_flex_var(); + let pattern_var = var_store.fresh(); let pattern_type = Type::Variable(pattern_var); let pattern_expected = PExpected::NoExpectation(pattern_type.clone()); @@ -504,7 +517,7 @@ fn canonicalize_expr( let can_arg = canonicalize_pattern( env, &mut state, - subs, + var_store, &mut scope, FunctionArg, &loc_pattern.value, @@ -518,7 +531,7 @@ fn canonicalize_expr( can_args.push(can_arg); } - let ret_var = subs.mk_flex_var(); + let ret_var = var_store.fresh(); let ret_type = Type::Variable(ret_var); state.vars.push(ret_var); @@ -529,7 +542,7 @@ fn canonicalize_expr( let (loc_body_expr, mut output) = canonicalize_expr( rigids, env, - subs, + var_store, &mut scope, loc_body_expr.region, &loc_body_expr.value, @@ -588,12 +601,12 @@ fn canonicalize_expr( ast::Expr::Case(loc_cond, branches) => { // Infer the condition expression's type. - let cond_var = subs.mk_flex_var(); + let cond_var = var_store.fresh(); let cond_type = Variable(cond_var); let (can_cond, mut output) = canonicalize_expr( rigids, env, - subs, + var_store, scope, region, &loc_cond.value, @@ -615,7 +628,7 @@ fn canonicalize_expr( let (can_pattern, loc_can_expr, branch_con, branch_references) = canonicalize_case_branch( env, - subs, + var_store, rigids, scope, region, @@ -649,7 +662,7 @@ fn canonicalize_expr( } _ => { - let branch_var = subs.mk_flex_var(); + let branch_var = var_store.fresh(); let branch_type = Variable(branch_var); let mut branch_cons = Vec::with_capacity(branches.len()); @@ -661,7 +674,7 @@ fn canonicalize_expr( let (can_pattern, loc_can_expr, branch_con, branch_references) = canonicalize_case_branch( env, - subs, + var_store, rigids, scope, region, @@ -737,17 +750,17 @@ fn canonicalize_expr( } ast::Expr::BinaryInt(string) => { let (constraint, answer) = - int_expr_from_result(subs, finish_parsing_bin(string), env, expected, region); + int_expr_from_result(var_store, finish_parsing_bin(string), env, expected, region); (answer, Output::new(constraint)) } ast::Expr::HexInt(string) => { let (constraint, answer) = - int_expr_from_result(subs, finish_parsing_hex(string), env, expected, region); + int_expr_from_result(var_store, finish_parsing_hex(string), env, expected, region); (answer, Output::new(constraint)) } ast::Expr::OctalInt(string) => { let (constraint, answer) = - int_expr_from_result(subs, finish_parsing_oct(string), env, expected, region); + int_expr_from_result(var_store, finish_parsing_oct(string), env, expected, region); (answer, Output::new(constraint)) } // Below this point, we shouln't see any of these nodes anymore because @@ -805,7 +818,7 @@ fn canonicalize_expr( #[inline(always)] fn canonicalize_case_branch<'a>( env: &mut Env, - subs: &mut Subs, + var_store: &VarStore, rigids: &Rigids, scope: &Scope, region: Region, @@ -836,7 +849,7 @@ fn canonicalize_case_branch<'a>( let (can_expr, branch_output) = canonicalize_expr( rigids, env, - subs, + var_store, &mut scope, region, &loc_expr.value, @@ -871,7 +884,7 @@ fn canonicalize_case_branch<'a>( let loc_can_pattern = canonicalize_pattern( env, &mut state, - subs, + var_store, &mut scope, CaseBranch, &loc_pattern.value, @@ -1363,7 +1376,7 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap) -> fn can_defs<'a>( rigids: &Rigids, env: &mut Env, - subs: &mut Subs, + var_store: &VarStore, scope: Scope, defs: &'a bumpalo::collections::Vec<'a, &'a Located>>, expected: Expected, @@ -1394,7 +1407,7 @@ fn can_defs<'a>( for loc_def in iter { // Make types for the body expr, even if we won't end up having a body. - let expr_var = subs.mk_flex_var(); + let expr_var = var_store.fresh(); let expr_type = Type::Variable(expr_var); // Each def gets to have all the idents in scope that are defined in this @@ -1437,7 +1450,7 @@ fn can_defs<'a>( let mut shadowable_idents = scope.idents.clone(); remove_idents(&loc_pattern.value, &mut shadowable_idents); - let pattern_var = subs.mk_flex_var(); + let pattern_var = var_store.fresh(); let pattern_type = Type::Variable(pattern_var); let pattern_expected = PExpected::NoExpectation(pattern_type); @@ -1450,7 +1463,7 @@ fn can_defs<'a>( let loc_can_pattern = canonicalize_pattern( env, &mut state, - subs, + var_store, &mut scope, Assignment, &loc_pattern.value, @@ -1487,7 +1500,7 @@ fn can_defs<'a>( let (mut loc_can_expr, can_output) = canonicalize_expr( rigids, env, - subs, + var_store, &mut scope, loc_expr.region, &loc_expr.value, @@ -1625,7 +1638,7 @@ fn can_defs<'a>( let (ret_expr, mut output) = canonicalize_expr( rigids, env, - subs, + var_store, &mut scope, loc_ret.region, &loc_ret.value, @@ -1758,7 +1771,7 @@ fn can_defs<'a>( } ( - Defs(subs.mk_flex_var(), can_defs, Box::new(ret_expr)), + Defs(var_store.fresh(), can_defs, Box::new(ret_expr)), output, ) } diff --git a/src/can/num.rs b/src/can/num.rs index d5ff8e34c8..1da73a66cf 100644 --- a/src/can/num.rs +++ b/src/can/num.rs @@ -4,7 +4,7 @@ use crate::can::problem::Problem; use crate::can::problem::RuntimeError::*; use crate::constrain; use crate::region::Region; -use crate::subs::Subs; +use crate::subs::VarStore; use crate::types::Constraint::{self, *}; use crate::types::Expected; use crate::types::Type; @@ -12,7 +12,7 @@ use std::i64; #[inline(always)] pub fn int_expr_from_result( - subs: &mut Subs, + var_store: &VarStore, result: Result, env: &mut Env, expected: Expected, @@ -20,7 +20,7 @@ pub fn int_expr_from_result( ) -> (Constraint, Expr) { match result { Ok(int) => ( - constrain::int_literal(subs, expected, region), + constrain::int_literal(var_store, expected, region), Expr::Int(int), ), Err(raw) => { @@ -35,7 +35,7 @@ pub fn int_expr_from_result( #[inline(always)] pub fn float_expr_from_result( - subs: &mut Subs, + var_store: &VarStore, result: Result, env: &mut Env, expected: Expected, @@ -43,7 +43,7 @@ pub fn float_expr_from_result( ) -> (Constraint, Expr) { match result { Ok(float) => ( - constrain::float_literal(subs, expected, region), + constrain::float_literal(var_store, expected, region), Expr::Float(float), ), Err(raw) => { diff --git a/src/can/pattern.rs b/src/can/pattern.rs index b1282f25b1..5e7031ffa1 100644 --- a/src/can/pattern.rs +++ b/src/can/pattern.rs @@ -10,7 +10,7 @@ use crate::collections::ImMap; use crate::ident::{Ident, VariantName}; use crate::parse::ast; use crate::region::{Located, Region}; -use crate::subs::Subs; +use crate::subs::VarStore; use crate::subs::Variable; use crate::types::{Constraint, PExpected, PatternCategory, Type}; @@ -50,7 +50,7 @@ pub enum PatternType { pub fn canonicalize_pattern<'a>( env: &'a mut Env, state: &'a mut PatternState, - subs: &mut Subs, + var_store: &VarStore, scope: &mut Scope, pattern_type: PatternType, pattern: &'a ast::Pattern<'a>, @@ -125,7 +125,7 @@ pub fn canonicalize_pattern<'a>( .insert(new_ident.clone(), symbol_and_region.clone()); shadowable_idents.insert(new_ident, symbol_and_region.clone()); - Pattern::Identifier(subs.mk_flex_var(), symbol) + Pattern::Identifier(var_store.fresh(), symbol) } } } @@ -168,7 +168,7 @@ pub fn canonicalize_pattern<'a>( if env.variants.contains_key(&symbol) { // No problems; the qualified variant name was in scope! - Pattern::Variant(subs.mk_flex_var(), symbol) + Pattern::Variant(var_store.fresh(), symbol) } else { let loc_name = Located { region, @@ -192,7 +192,7 @@ pub fn canonicalize_pattern<'a>( }, &Underscore => match pattern_type { - CaseBranch | FunctionArg => Pattern::Underscore(subs.mk_flex_var()), + CaseBranch | FunctionArg => Pattern::Underscore(var_store.fresh()), Assignment => unsupported_pattern(env, Assignment, region), }, @@ -249,7 +249,7 @@ pub fn canonicalize_pattern<'a>( return canonicalize_pattern( env, state, - subs, + var_store, scope, pattern_type, sub_pattern, diff --git a/src/constrain.rs b/src/constrain.rs index 650b04f154..e19668a3bc 100644 --- a/src/constrain.rs +++ b/src/constrain.rs @@ -1,6 +1,6 @@ use crate::collections::ImMap; use crate::region::Region; -use crate::subs::{Subs, Variable}; +use crate::subs::{VarStore, Variable}; use crate::types::Constraint::{self, *}; use crate::types::Expected::{self, *}; use crate::types::Type::{self, *}; @@ -16,30 +16,30 @@ pub fn exists(flex_vars: Vec, constraint: Constraint) -> Constraint { })) } -pub fn int_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { +pub fn int_literal(var_store: &VarStore, expected: Expected, region: Region) -> Constraint { let typ = number_literal_type("Int", "Integer"); let reason = Reason::IntLiteral; - num_literal(subs, typ, reason, expected, region) + num_literal(var_store, typ, reason, expected, region) } #[inline(always)] -pub fn float_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { +pub fn float_literal(var_store: &VarStore, expected: Expected, region: Region) -> Constraint { let typ = number_literal_type("Float", "FloatingPoint"); let reason = Reason::FloatLiteral; - num_literal(subs, typ, reason, expected, region) + num_literal(var_store, typ, reason, expected, region) } #[inline(always)] fn num_literal( - subs: &mut Subs, + var_store: &VarStore, literal_type: Type, reason: Reason, expected: Expected, region: Region, ) -> Constraint { - let num_var = subs.mk_flex_var(); + let num_var = var_store.fresh(); let num_type = Variable(num_var); let expected_literal = ForReason(reason, literal_type, region); diff --git a/src/subs.rs b/src/subs.rs index 88b4ca537c..97b40a89e6 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -2,12 +2,40 @@ use crate::ena::unify::{InPlace, UnificationTable, UnifyKey}; use crate::types::Problem; use crate::unify; use std::fmt; +use std::sync::atomic::{AtomicUsize, Ordering}; #[derive(Debug, Default)] pub struct Subs { utable: UnificationTable>, } +pub struct VarStore { + next: AtomicUsize, +} + +impl VarStore { + pub fn new() -> Self { + VarStore { + next: AtomicUsize::new(0), + } + } + + pub fn fresh(&self) -> Variable { + // Increment the counter and return the previous value. + // + // Since the counter starts at 0, this will return 0 on first invocation, + // and var_store.into() will return the number of Variables distributed + // (in this case, 1). + Variable(AtomicUsize::fetch_add(&self.next, 1, Ordering::Relaxed)) + } +} + +impl Into for VarStore { + fn into(self) -> Variable { + Variable(self.next.into_inner()) + } +} + #[derive(Copy, PartialEq, Eq, Clone, Hash)] pub struct Variable(usize);