Canonicalize with VarStore over Subs

This commit is contained in:
Richard Feldman 2019-11-30 20:56:53 -05:00
parent acc0933c65
commit e1fb0fa4e9
5 changed files with 99 additions and 58 deletions

View file

@ -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<Box<str>, Type>;
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_declaration<'a>(
arena: &Bump,
subs: &mut Subs,
var_store: &VarStore,
home: Box<str>,
name: Box<str>,
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<Symbol, References>) ->
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<Def<'a>>>,
expected: Expected<Type>,
@ -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,
)
}

View file

@ -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<i64, &str>,
env: &mut Env,
expected: Expected<Type>,
@ -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<f64, &str>,
env: &mut Env,
expected: Expected<Type>,
@ -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) => {

View file

@ -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,

View file

@ -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<Variable>, constraint: Constraint) -> Constraint {
}))
}
pub fn int_literal(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
pub fn int_literal(var_store: &VarStore, expected: Expected<Type>, 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<Type>, region: Region) -> Constraint {
pub fn float_literal(var_store: &VarStore, expected: Expected<Type>, 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<Type>,
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);

View file

@ -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<InPlace<Variable>>,
}
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<Variable> for VarStore {
fn into(self) -> Variable {
Variable(self.next.into_inner())
}
}
#[derive(Copy, PartialEq, Eq, Clone, Hash)]
pub struct Variable(usize);