Start merging constraint gen into canonicalization

This commit is contained in:
Richard Feldman 2019-10-16 19:21:39 -04:00
parent 4704799df5
commit ae301f3c86
19 changed files with 989 additions and 829 deletions

View file

@ -1,5 +1,3 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use can::expr::Expr;
use can::pattern::Pattern;
use can::problem::Problem;
@ -7,55 +5,44 @@ use can::procedure::{Procedure, References};
use can::symbol::Symbol;
use collections::{ImMap, MutMap};
use region::{Located, Region};
use subs::Subs;
/// The canonicalization environment for a particular module.
pub struct Env<'a> {
pub struct Env {
/// The module's path. Unqualified references to identifiers and variant names are assumed
/// to be relative to this path.
pub home: &'a str,
pub home: Box<str>,
/// Problems we've encountered along the way, which will be reported to the user at the end.
pub problems: Vec<'a, Problem<'a>>,
pub problems: Vec<Problem>,
/// Variants either declared in this module, or imported.
pub variants: ImMap<Symbol<'a>, Located<&'a str>>,
pub variants: ImMap<Symbol, Located<Box<str>>>,
/// Former closures converted to top-level procedures.
pub procedures: MutMap<Symbol<'a>, Procedure<'a>>,
pub arena: &'a Bump,
pub subs: Subs<'a>,
pub procedures: MutMap<Symbol, Procedure>,
}
impl<'a> Env<'a> {
pub fn new(
arena: &'a Bump,
home: &'a str,
declared_variants: ImMap<Symbol<'a>, Located<&'a str>>,
) -> Env<'a> {
impl Env {
pub fn new(home: Box<str>, declared_variants: ImMap<Symbol, Located<Box<str>>>) -> Env {
Env {
home,
variants: declared_variants,
problems: Vec::new_in(arena),
problems: Vec::new(),
procedures: MutMap::default(),
arena,
subs: Subs::new(arena),
}
}
pub fn problem(&mut self, problem: Problem<'a>) -> () {
pub fn problem(&mut self, problem: Problem) -> () {
self.problems.push(problem)
}
pub fn register_closure(
&mut self,
symbol: Symbol<'a>,
args: &'a [Located<Pattern<'a>>],
body: Located<Expr<'a>>,
symbol: Symbol,
args: Vec<Located<Pattern>>,
body: Located<Expr>,
definition: Region,
references: References<'a>,
references: References,
) -> () {
// We can't if the closure is self tail recursive yet, because it doesn't know its final name yet.
// (Assign sets that.) Assume this is false, and let Assign change it to true after it sets final name.

View file

@ -1,49 +1,44 @@
use can::pattern::Pattern;
use can::problem::RuntimeError;
use can::symbol::Symbol;
use operator::Operator;
use region::Located;
use std::i64;
use subs::Variable;
#[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> {
pub enum Expr {
// Literals
Int(Variable, i64),
Float(Variable, f64),
Str(Variable, &'a str),
List(Variable, &'a [Located<Expr<'a>>]),
Int(i64),
Float(f64),
Str(Box<str>),
List(Variable, Vec<Located<Expr>>),
EmptyList,
// Lookups
Var(Variable, Symbol<'a>),
Var(Variable, Symbol),
/// Works the same as Var, but has an important marking purpose.
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
FunctionPointer(Variable, Symbol<'a>),
FunctionPointer(Variable, Symbol),
// Pattern Matching
Case(
Variable,
&'a Located<Expr<'a>>,
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
Box<Located<Expr>>,
Vec<(Located<Pattern>, Located<Expr>)>,
),
Define(
Variable,
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
&'a Located<Expr<'a>>,
Vec<(Located<Pattern>, Located<Expr>)>,
Box<Located<Expr>>,
),
// Application
Call(Variable, &'a Located<Expr<'a>>, &'a [Located<Expr<'a>>]),
// This has to be separate from Call so we can do precedence reordering
Operator(
Variable,
&'a (Located<Expr<'a>>, Located<Operator>, Located<Expr<'a>>),
),
Call(Variable, Box<Located<Expr>>, Vec<Located<Expr>>),
// Product Types
Record(Variable, &'a [Located<(&'a str, Located<Expr<'a>>)>]),
Record(Variable, Vec<Located<(Box<str>, Located<Expr>)>>),
EmptyRecord,
// Compiles, but will crash if reached
RuntimeError(Variable, RuntimeError<'a>),
RuntimeError(RuntimeError),
}

File diff suppressed because it is too large Load diff

View file

@ -3,21 +3,23 @@ use can::problem::Problem;
use can::scope::Scope;
use can::symbol::Symbol;
use collections::ImMap;
use constrain::{self, Constraints};
use ident::{Ident, VariantName};
use parse::ast;
use region::{Located, Region};
use subs::Subs;
use subs::Variable;
/// A pattern, including possible problems (e.g. shadowing) so that
/// codegen can generate a runtime error if this pattern is reached.
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern<'a> {
Identifier(Variable, Symbol<'a>),
Variant(Variable, Symbol<'a>),
AppliedVariant(Variable, Symbol<'a>, Vec<Located<Pattern<'a>>>),
pub enum Pattern {
Identifier(Variable, Symbol),
Variant(Variable, Symbol),
AppliedVariant(Variable, Symbol, Vec<Located<Pattern>>),
IntLiteral(Variable, i64),
FloatLiteral(Variable, f64),
ExactString(Variable, &'a str),
ExactString(Variable, Box<str>),
EmptyRecordLiteral(Variable),
Underscore(Variable),
@ -40,12 +42,14 @@ pub enum PatternType {
}
pub fn canonicalize_pattern<'a>(
env: &'a mut Env<'a>,
scope: &'a mut Scope<'a>,
env: &'a mut Env,
subs: &mut Subs,
constraints: &Constraints,
scope: &mut Scope,
pattern_type: &'a PatternType,
loc_pattern: &'a Located<ast::Pattern<'a>>,
shadowable_idents: &'a mut ImMap<Ident, (Symbol<'a>, Region)>,
) -> Located<Pattern<'a>> {
shadowable_idents: &'a mut ImMap<Ident, (Symbol, Region)>,
) -> Located<Pattern> {
use self::PatternType::*;
use can::ast::Pattern::*;
@ -114,7 +118,7 @@ pub fn canonicalize_pattern<'a>(
.insert(new_ident.clone(), symbol_and_region.clone());
shadowable_idents.insert(new_ident, symbol_and_region);
Pattern::Identifier(env.subs.mk_flex_var(), symbol)
Pattern::Identifier(subs.mk_flex_var(), symbol)
}
}
}
@ -127,7 +131,7 @@ pub fn canonicalize_pattern<'a>(
// for loc_arg in loc_args {
// let loc_can_arg =
// canonicalize_pattern(env, scope, pattern_type, &loc_arg, shadowable_idents);
// canonicalize_pattern(env, subs, constraints, scope, pattern_type, &loc_arg, shadowable_idents);
// can_args.push(loc_can_arg);
// }
@ -157,7 +161,7 @@ pub fn canonicalize_pattern<'a>(
if env.variants.contains_key(&symbol) {
// No problems; the qualified variant name was in scope!
Pattern::Variant(env.subs.mk_flex_var(), symbol)
Pattern::Variant(subs.mk_flex_var(), symbol)
} else {
let loc_name = Located {
region: region.clone(),
@ -185,7 +189,7 @@ pub fn canonicalize_pattern<'a>(
// ptype @ Assignment | ptype @ FunctionArg => unsupported_pattern(env, *ptype, region),
// },
&Underscore => match pattern_type {
CaseBranch | FunctionArg => Pattern::Underscore(env.subs.mk_flex_var()),
CaseBranch | FunctionArg => Pattern::Underscore(subs.mk_flex_var()),
Assignment => unsupported_pattern(env, Assignment, region.clone()),
},
@ -201,11 +205,7 @@ pub fn canonicalize_pattern<'a>(
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
fn unsupported_pattern<'a>(
env: &'a mut Env<'a>,
pattern_type: PatternType,
region: Region,
) -> Pattern<'a> {
fn unsupported_pattern<'a>(env: &'a mut Env, pattern_type: PatternType, region: Region) -> Pattern {
env.problem(Problem::UnsupportedPattern(pattern_type, region.clone()));
Pattern::UnsupportedPattern(region)

217
src/can/precedence.rs Normal file
View file

@ -0,0 +1,217 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use operator::Operator;
use parse::ast::Expr;
use region::{Located, Region};
// Operator precedence logic adapted from Gluon by Markus Westerlind, MIT licensed
// https://github.com/gluon-lang/gluon
// Thank you, Markus!
fn new_op_expr<'a>(
arena: &'a Bump,
left: Located<Expr<'a>>,
op: Located<Operator>,
right: Located<Expr<'a>>,
) -> Located<Expr<'a>> {
let new_region = Region {
start_line: left.region.start_line,
start_col: left.region.start_col,
end_line: right.region.end_line,
end_col: right.region.end_col,
};
let new_expr = Expr::Operator(arena.alloc((left, op, right)));
Located {
value: new_expr,
region: new_region,
}
}
/// Reorder the expression tree based on operator precedence and associativity rules.
pub fn apply_precedence_and_associativity<'a>(
arena: &'a Bump,
expr: Located<Expr<'a>>,
) -> Located<Expr<'a>> {
use operator::Associativity::*;
use std::cmp::Ordering;
// NOTE: A potentially nice performance optimization here would be to use
// arena bump allocation for Infixes, arg_stack, and op_stack. As long as we
// allocate each element inside arg_stack outside the arena, this should end
// up being a decent bit more efficient.
let mut infixes = Infixes::new(arena.alloc(expr));
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
let mut op_stack: Vec<Located<Operator>> = Vec::new_in(arena);
while let Some(token) = infixes.next() {
match token {
InfixToken::Arg(next_expr) => arg_stack.push(next_expr),
InfixToken::Op(next_op) => {
match op_stack.pop() {
Some(stack_op) => {
match next_op.value.cmp(&stack_op.value) {
Ordering::Less => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
left.clone(),
stack_op,
right.clone(),
)));
}
Ordering::Greater => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
Ordering::Equal => {
match (
next_op.value.associativity(),
stack_op.value.associativity(),
) {
(LeftAssociative, LeftAssociative) => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
left.clone(),
stack_op,
right.clone(),
)));
}
(RightAssociative, RightAssociative) => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
(NonAssociative, NonAssociative) => {
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
let bad_op = next_op.clone();
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
let broken_expr = new_op_expr(
arena,
left.clone(),
next_op,
right.clone(),
);
let region = broken_expr.region.clone();
let value = Expr::PrecedenceConflict(
bad_op,
stack_op,
arena.alloc(broken_expr),
);
return Located { region, value };
}
_ => {
// The operators had the same precedence but different associativity.
//
// In many languages, this case can happen due to (for example) <| and |> having the same
// precedence but different associativity. Languages which support custom operators with
// (e.g. Haskell) can potentially have arbitrarily many of these cases.
//
// By design, Roc neither allows custom operators nor has any built-in operators with
// the same precedence and different associativity, so this should never happen!
panic!("Operators had the same associativity, but different precedence. This should never happen!");
}
}
}
}
}
None => op_stack.push(next_op),
};
}
}
}
for op in op_stack.into_iter().rev() {
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
arg_stack.push(arena.alloc(new_op_expr(arena, left.clone(), op, right.clone())));
}
assert_eq!(arg_stack.len(), 1);
arg_stack.pop().unwrap().clone()
}
#[derive(Debug, Clone, PartialEq)]
enum InfixToken<'a> {
Arg(&'a Located<Expr<'a>>),
Op(Located<Operator>),
}
/// An iterator that takes an expression that has had its operators grouped
/// with _right associativity_, and yeilds a sequence of `InfixToken`s. This
/// is useful for reparsing the operators with their correct associativies
/// and precedences.
///
/// For example, the expression:
///
/// ```text
/// (1 + (2 ^ (4 * (6 - 8))))
/// ```
///
/// Will result in the following iterations:
///
/// ```text
/// Arg: 1
/// Op: +
/// Arg: 2
/// Op: ^
/// Arg: 4
/// Op: *
/// Arg: 6
/// Op: -
/// Arg: 8
/// ```
struct Infixes<'a> {
/// The next part of the expression that we need to flatten
remaining_expr: Option<&'a Located<Expr<'a>>>,
/// Cached operator from a previous iteration
next_op: Option<Located<Operator>>,
}
impl<'a> Infixes<'a> {
fn new(expr: &'a Located<Expr<'a>>) -> Infixes<'a> {
Infixes {
remaining_expr: Some(expr),
next_op: None,
}
}
}
impl<'a> Iterator for Infixes<'a> {
type Item = InfixToken<'a>;
fn next(&mut self) -> Option<InfixToken<'a>> {
match self.next_op.take() {
Some(op) => Some(InfixToken::Op(op)),
None => self.remaining_expr.take().map(|expr| match expr.value {
Expr::Operator((left, op, right)) => {
self.remaining_expr = Some(right);
self.next_op = Some(op.clone());
InfixToken::Arg(left)
}
_ => InfixToken::Arg(expr),
}),
}
}
}

View file

@ -1,13 +1,11 @@
use bumpalo::collections::Vec;
use can::expr::Expr;
use can::pattern::{Pattern, PatternType};
use can::pattern::PatternType;
use ident::{Ident, VariantName};
use operator::Operator;
use region::{Located, Region};
/// Problems that can occur in the course of canonicalization.
#[derive(Clone, Debug, PartialEq)]
pub enum Problem<'a> {
pub enum Problem {
Shadowing(Located<Ident>),
UnrecognizedFunctionName(Located<Ident>),
UnrecognizedConstant(Located<Ident>),
@ -17,8 +15,8 @@ pub enum Problem<'a> {
PrecedenceProblem(PrecedenceProblem),
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(PatternType, Region),
CircularAssignment(Vec<'a, Located<Ident>>),
RuntimeError(RuntimeError<'a>),
CircularAssignment(Vec<Located<Ident>>),
RuntimeError(RuntimeError),
}
#[derive(Clone, Debug, PartialEq)]
@ -27,20 +25,20 @@ pub enum PrecedenceProblem {
}
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError<'a> {
InvalidPrecedence(PrecedenceProblem, &'a Located<Expr<'a>>),
pub enum RuntimeError {
InvalidPrecedence(PrecedenceProblem, Region),
UnrecognizedFunctionName(Located<Ident>),
UnrecognizedConstant(Located<Ident>),
UnrecognizedVariant(Located<VariantName>),
FloatOutsideRange(&'a str),
IntOutsideRange(&'a str),
InvalidHex(std::num::ParseIntError, &'a str),
InvalidOctal(std::num::ParseIntError, &'a str),
InvalidBinary(std::num::ParseIntError, &'a str),
FloatOutsideRange(Box<str>),
IntOutsideRange(Box<str>),
InvalidHex(std::num::ParseIntError, Box<str>),
InvalidOctal(std::num::ParseIntError, Box<str>),
InvalidBinary(std::num::ParseIntError, Box<str>),
CircularAssignment(
Vec<'a, Located<Ident>>,
Vec<'a, (Located<Pattern<'a>>, Located<Expr<'a>>)>,
&'a Located<Expr<'a>>,
Vec<Located<Ident>>,
Vec<(Region /* pattern */, Region /* expr */)>,
Region,
),
/// When the author specifies a type annotation but no implementation

View file

@ -5,22 +5,22 @@ use collections::ImSet;
use region::{Located, Region};
#[derive(Clone, Debug, PartialEq)]
pub struct Procedure<'a> {
pub name: Option<&'a str>,
pub struct Procedure {
pub name: Option<Box<str>>,
pub is_self_tail_recursive: bool,
pub definition: Region,
pub args: &'a [Located<Pattern<'a>>],
pub body: Located<Expr<'a>>,
pub references: References<'a>,
pub args: Vec<Located<Pattern>>,
pub body: Located<Expr>,
pub references: References,
}
impl<'a> Procedure<'a> {
impl Procedure {
pub fn new(
definition: Region,
args: &'a [Located<Pattern<'a>>],
body: Located<Expr<'a>>,
references: References<'a>,
) -> Procedure<'a> {
args: Vec<Located<Pattern>>,
body: Located<Expr>,
references: References,
) -> Procedure {
Procedure {
name: None,
is_self_tail_recursive: false,
@ -36,15 +36,15 @@ impl<'a> Procedure<'a> {
/// to determine how assignments shuold be ordered. We want builds to be reproducible,
/// so it's important that building the same code gives the same order every time!
#[derive(Clone, Debug, PartialEq)]
pub struct References<'a> {
pub locals: ImSet<Symbol<'a>>,
pub globals: ImSet<Symbol<'a>>,
pub variants: ImSet<Symbol<'a>>,
pub calls: ImSet<Symbol<'a>>,
pub struct References {
pub locals: ImSet<Symbol>,
pub globals: ImSet<Symbol>,
pub variants: ImSet<Symbol>,
pub calls: ImSet<Symbol>,
}
impl<'a> References<'a> {
pub fn new() -> References<'a> {
impl References {
pub fn new() -> References {
References {
locals: ImSet::default(),
globals: ImSet::default(),
@ -53,7 +53,7 @@ impl<'a> References<'a> {
}
}
pub fn union(mut self, other: References<'a>) -> Self {
pub fn union(mut self, other: References) -> Self {
self.locals = self.locals.union(other.locals);
self.globals = self.globals.union(other.globals);
self.variants = self.variants.union(other.variants);
@ -62,11 +62,11 @@ impl<'a> References<'a> {
self
}
pub fn has_local(&self, symbol: &Symbol<'a>) -> bool {
pub fn has_local(&self, symbol: &Symbol) -> bool {
self.locals.contains(symbol)
}
pub fn has_variant(&self, symbol: &Symbol<'a>) -> bool {
pub fn has_variant(&self, symbol: &Symbol) -> bool {
self.variants.contains(symbol)
}
}

View file

@ -4,17 +4,14 @@ use ident::Ident;
use region::Region;
#[derive(Clone, Debug, PartialEq)]
pub struct Scope<'a> {
pub idents: ImMap<Ident, (Symbol<'a>, Region)>,
symbol_prefix: &'a str,
pub struct Scope {
pub idents: ImMap<Ident, (Symbol, Region)>,
symbol_prefix: Box<str>,
next_unique_id: u64,
}
impl<'a> Scope<'a> {
pub fn new(
symbol_prefix: &'a str,
declared_idents: ImMap<Ident, (Symbol<'a>, Region)>,
) -> Scope<'a> {
impl Scope {
pub fn new(symbol_prefix: Box<str>, declared_idents: ImMap<Ident, (Symbol, Region)>) -> Scope {
Scope {
symbol_prefix,
@ -26,11 +23,11 @@ impl<'a> Scope<'a> {
}
}
pub fn symbol(&'a self, name: &'a str) -> Symbol<'a> {
pub fn symbol(&self, name: &str) -> Symbol {
Symbol::new(&self.symbol_prefix, name)
}
pub fn gen_unique_symbol(&mut self) -> Symbol<'a> {
pub fn gen_unique_symbol(&mut self) -> Symbol {
self.next_unique_id = self.next_unique_id + 1;
Symbol::new(&self.symbol_prefix, &self.next_unique_id.to_string())

View file

@ -3,14 +3,14 @@ use ident::VariantName;
/// A globally unique identifier, used for both vars and variants.
/// It will be used directly in code gen.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Symbol<'a>(&'a str);
pub struct Symbol(Box<str>);
impl<'a> Symbol<'a> {
pub fn new(prefix: &'a str, name: &'a str) -> Symbol<'a> {
Symbol(&format!("{}{}", prefix, name))
impl Symbol {
pub fn new(prefix: &str, name: &str) -> Symbol {
Symbol(format!("{}{}", prefix, name).into())
}
pub fn from_variant(variant_name: &'a VariantName, home: &'a str) -> Symbol<'a> {
pub fn from_variant(variant_name: &VariantName, home: &str) -> Symbol {
match &variant_name {
&VariantName::Unqualified(ref name) => Symbol::new(home, name),
@ -19,8 +19,8 @@ impl<'a> Symbol<'a> {
}
}
impl<'a> Into<&'a str> for Symbol<'a> {
fn into(self) -> &'a str {
impl Into<Box<str>> for Symbol {
fn into(self) -> Box<str> {
let Symbol(string) = self;
string

View file

@ -4,11 +4,12 @@ use can::procedure::Procedure;
// use can::symbol::Symbol;
use collections::ImMap;
// use operator::{ArgSide, Operator};
use region::Located;
use subs::Subs;
use types::Constraint;
use types::Expected;
use types::Type;
use region::{Located, Region};
use subs::{Subs, Variable};
use types::Constraint::{self, *};
use types::Expected::{self, *};
use types::Reason;
use types::Type::{self, *};
/// This lets us share bound type variables between nested annotations, e.g.
///
@ -19,41 +20,90 @@ use types::Type;
/// 42
///
/// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary.
type BoundTypeVars<'a> = ImMap<&'a str, Type<'a>>;
type BoundTypeVars<'a> = ImMap<&'a str, Type>;
pub struct Constraints(Vec<Constraint>);
impl Constraints {
pub fn new() -> Self {
Constraints(Vec::new())
}
pub fn add(&mut self, constraint: Constraint) {
self.0.push(constraint)
}
}
pub fn int_literal(
subs: &mut Subs,
constraints: &mut Constraints,
expected: Expected<Type>,
region: Region,
) {
let typ = number_literal_type("Int", "Integer");
let reason = Reason::IntLiteral;
num_literal(subs, constraints, typ, reason, expected, region)
}
#[inline(always)]
pub fn float_literal(
subs: &mut Subs,
constraints: &mut Constraints,
expected: Expected<Type>,
region: Region,
) {
let typ = number_literal_type("Float", "FloatingPoint");
let reason = Reason::FloatLiteral;
num_literal(subs, constraints, typ, reason, expected, region)
}
#[inline(always)]
fn num_literal(
subs: &mut Subs,
constraints: &mut Constraints,
literal_type: Type,
reason: Reason,
expected: Expected<Type>,
region: Region,
) {
let num_var = subs.mk_flex_var();
let num_type = Variable(num_var);
let expected_literal = ForReason(reason, literal_type, region.clone());
constraints.add(Eq(num_type.clone(), expected_literal, region.clone()));
constraints.add(Eq(num_type, expected, region.clone()));
}
#[inline(always)]
fn number_literal_type(module_name: &str, type_name: &str) -> Type {
builtin_type(
"Num",
"Num",
vec![builtin_type(module_name, type_name, Vec::new())],
)
}
#[inline(always)]
fn builtin_type(module_name: &str, type_name: &str, args: Vec<Type>) -> Type {
Type::Apply {
module_name: module_name.into(),
name: type_name.into(),
args,
}
}
pub fn constrain<'a>(
bound_vars: &'a BoundTypeVars<'a>,
subs: &'a mut Subs<'a>,
loc_expr: Located<Expr<'a>>,
expected: Expected<Type<'a>>,
) -> Constraint<'a> {
subs: &'a mut Subs,
loc_expr: Located<Expr>,
expected: Expected<Type>,
) -> Constraint {
panic!("TODO inline constrain");
// let region = loc_expr.region;
// match loc_expr.value {
// Int(_) => int_literal(subs, expected, region),
// Float(_) => float_literal(subs, expected, region),
// Str(_) => Eq(string(), expected, region),
// InterpolatedStr(pairs, _) => {
// let mut constraints = Vec::with_capacity(pairs.len() + 1);
// for (_, loc_interpolated_expr) in pairs {
// let expected_str = ForReason(
// Reason::InterpolatedStringVar,
// string(),
// loc_interpolated_expr.region.clone(),
// );
// let constraint = constrain(bound_vars, subs, loc_interpolated_expr, expected_str);
// constraints.push(constraint);
// }
// constraints.push(Eq(string(), expected, region));
// And(constraints)
// }
// EmptyRecord => Eq(EmptyRec, expected, region),
// EmptyList => Eq(empty_list(subs.mk_flex_var()), expected, region),
// List(elems) => list(elems, bound_vars, subs, expected, region),
// Var(sym) | FunctionPointer(sym) => Lookup(sym, expected, region),
// Assign(assignments, ret_expr) => {
@ -315,100 +365,52 @@ pub fn constrain<'a>(
// }
// }
// fn empty_list(var: Variable) -> Type {
// builtin_type("List", "List", vec![Type::Variable(var)])
// }
pub fn empty_list_type(var: Variable) -> Type {
list_type(Type::Variable(var))
}
// fn string() -> Type {
// builtin_type("Str", "Str", Vec::new())
// }
pub fn list_type(typ: Type) -> Type {
builtin_type("List", "List", vec![typ])
}
// fn _num(var: Variable) -> Type {
// builtin_type("Num", "Num", vec![Type::Variable(var)])
// }
pub fn str_type() -> Type {
builtin_type("Str", "Str", Vec::new())
}
// fn list(
// loc_elems: Vec<Located<Expr>>,
// bound_vars: &BoundTypeVars,
// subs: &mut Subs,
// expected: Expected<Type>,
// region: Region,
// ) -> Constraint {
// let list_var = subs.mk_flex_var(); // `v` in the type (List v)
// let list_type = Type::Variable(list_var);
// let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
fn list(
loc_elems: Vec<Located<Expr>>,
bound_vars: &BoundTypeVars,
subs: &mut Subs,
expected: Expected<Type>,
region: Region,
) -> Constraint {
let list_var = subs.mk_flex_var(); // `v` in the type (List v)
let list_type = Type::Variable(list_var);
let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
// for loc_elem in loc_elems {
// let elem_var = subs.mk_flex_var();
// let elem_type = Variable(elem_var);
// let elem_expected = NoExpectation(elem_type.clone());
// let elem_constraint = constrain(bound_vars, subs, loc_elem, elem_expected);
// let list_elem_constraint = Eq(
// list_type.clone(),
// ForReason(Reason::ElemInList, elem_type, region.clone()),
// region.clone(),
// );
for loc_elem in loc_elems {
let elem_var = subs.mk_flex_var();
let elem_type = Variable(elem_var);
let elem_expected = NoExpectation(elem_type.clone());
let elem_constraint = constrain(bound_vars, subs, loc_elem, elem_expected);
let list_elem_constraint = Eq(
list_type.clone(),
ForReason(Reason::ElemInList, elem_type, region.clone()),
region.clone(),
);
// constraints.push(elem_constraint);
// constraints.push(list_elem_constraint);
// }
constraints.push(elem_constraint);
constraints.push(list_elem_constraint);
}
// constraints.push(Eq(
// builtin_type("List", "List", vec![list_type]),
// expected,
// region,
// ));
constraints.push(Eq(
builtin_type("List", "List", vec![list_type]),
expected,
region,
));
// And(constraints)
// }
// #[inline(always)]
// fn int_literal(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
// let typ = number_literal_type("Int", "Integer");
// let reason = Reason::IntLiteral;
// num_literal(typ, reason, subs, expected, region)
// }
// #[inline(always)]
// fn float_literal(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
// let typ = number_literal_type("Float", "FloatingPoint");
// let reason = Reason::FloatLiteral;
// num_literal(typ, reason, subs, expected, region)
// }
// #[inline(always)]
// fn num_literal(
// literal_type: Type,
// reason: Reason,
// subs: &mut Subs,
// expected: Expected<Type>,
// region: Region,
// ) -> Constraint {
// let num_var = subs.mk_flex_var();
// let num_type = Variable(num_var);
// let expected_literal = ForReason(reason, literal_type, region.clone());
// And(vec![
// Eq(num_type.clone(), expected_literal, region.clone()),
// Eq(num_type, expected, region.clone()),
// ])
// }
// #[inline(always)]
// fn number_literal_type(module_name: &str, type_name: &str) -> Type {
// builtin_type(
// "Num",
// "Num",
// vec![builtin_type(module_name, type_name, Vec::new())],
// )
// }
// #[inline(always)]
// fn builtin_type(module_name: &str, type_name: &str, args: Vec<Type>) -> Type {
// Type::Apply(module_name.to_string(), type_name.to_string(), args)
// }
And(constraints)
}
// pub fn constrain_def(
// loc_pattern: Located<Pattern>,
@ -484,10 +486,10 @@ pub fn constrain<'a>(
pub fn constrain_procedure<'a>(
bound_vars: &'a BoundTypeVars<'a>,
subs: &'a mut Subs<'a>,
proc: Procedure<'a>,
expected: Expected<Type<'a>>,
) -> Constraint<'a> {
subs: &'a mut Subs,
proc: Procedure,
expected: Expected<Type>,
) -> Constraint {
panic!("TODO inline constrain_procedure");
// let mut state = PatternState {
// assignment_types: ImMap::default(),

View file

@ -11,9 +11,9 @@ use types::Expected::*;
use types::Type::*;
pub fn infer_expr<'a>(
subs: &'a mut Subs<'a>,
loc_expr: Located<Expr<'a>>,
procedures: MutMap<Symbol<'a>, Procedure<'a>>,
subs: &'a mut Subs,
loc_expr: Located<Expr>,
procedures: MutMap<Symbol, Procedure>,
) -> Content {
panic!("TODO re-constrain procedures.");
// let bound_vars = ImMap::default();

View file

@ -75,7 +75,7 @@ impl Operator {
}
}
pub fn desugar(&self) -> Symbol<'static> {
pub fn desugar(&self) -> Symbol {
match self {
Caret => Symbol::new("Num", "pow"),
Star => Symbol::new("Num", "mul"),

View file

@ -83,6 +83,9 @@ pub enum Expr<'a> {
// Problems
MalformedIdent(&'a str),
MalformedClosure,
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(Loc<Operator>, Loc<Operator>, &'a Loc<Expr<'a>>),
}
#[derive(Debug, Clone, PartialEq)]

View file

@ -292,6 +292,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
| Expr::If(_)
| Expr::Case(_, _)
| Expr::MalformedClosure
| Expr::PrecedenceConflict(_, _, _)
| Expr::QualifiedField(_, _) => Err(Fail {
attempting: Attempting::Def,
reason: FailReason::InvalidPattern,

View file

@ -5,143 +5,151 @@ static EMPTY_RECORD: &'static str = "{}";
pub fn content_to_string(content: Content, subs: &mut Subs) -> String {
let mut buf = String::new();
panic!("TODO restore content_to_string()");
// write_content(content, subs, &mut buf, false);
write_content(content, subs, &mut buf, false);
buf
}
// fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) {
// use subs::Content::*;
fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) {
use subs::Content::*;
// match content {
// FlexVar(Some(name)) => buf.push_str(&name),
// FlexVar(None) => buf.push_str(WILDCARD),
// RigidVar(name) => buf.push_str(&name),
// Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens),
// Error(_) => buf.push_str("<type mismatch>"),
// }
// }
match content {
FlexVar(Some(name)) => buf.push_str(&name),
FlexVar(None) => buf.push_str(WILDCARD),
RigidVar(name) => buf.push_str(&name),
Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens),
Error(_) => buf.push_str("<type mismatch>"),
}
}
// fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) {
// use subs::FlatType::*;
fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) {
use subs::FlatType::*;
// match flat_type {
// Apply(module_name, type_name, args) => {
// write_apply(module_name, type_name, args, subs, buf, use_parens)
// }
// EmptyRecord => buf.push_str(EMPTY_RECORD),
// Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
// Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
// Erroneous(problem) => {
// buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
// }
// }
// }
match flat_type {
Apply {
module_name,
name,
args,
} => write_apply(
module_name.to_string(),
name.to_string(),
args,
subs,
buf,
use_parens,
),
EmptyRecord => buf.push_str(EMPTY_RECORD),
Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
Erroneous(problem) => {
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
}
}
}
// fn write_apply(
// module_name: String,
// type_name: String,
// args: Vec<Variable>,
// subs: &mut Subs,
// buf: &mut String,
// use_parens: bool,
// ) {
// let write_parens = use_parens && !args.is_empty();
fn write_apply(
module_name: String,
type_name: String,
args: Vec<Variable>,
subs: &mut Subs,
buf: &mut String,
use_parens: bool,
) {
let write_parens = use_parens && !args.is_empty();
// // Hardcoded type aliases
// if module_name == "Str" && type_name == "Str" {
// buf.push_str("Str");
// } else if module_name == "Num" && type_name == "Num" {
// let arg = args
// .into_iter()
// .next()
// .unwrap_or_else(|| panic!("Num did not have any type parameters somehow."));
// let arg_content = subs.get(arg).content;
// let mut arg_param = String::new();
// Hardcoded type aliases
if module_name == "Str" && type_name == "Str" {
buf.push_str("Str");
} else if module_name == "Num" && type_name == "Num" {
let arg = args
.into_iter()
.next()
.unwrap_or_else(|| panic!("Num did not have any type parameters somehow."));
let arg_content = subs.get(arg).content;
let mut arg_param = String::new();
// write_content(arg_content, subs, &mut arg_param, true);
write_content(arg_content, subs, &mut arg_param, true);
// if arg_param == "Int.Integer" {
// buf.push_str("Int");
// } else if arg_param == "Float.FloatingPoint" {
// buf.push_str("Float");
// } else {
// if write_parens {
// buf.push_str("(");
// }
if arg_param == "Int.Integer" {
buf.push_str("Int");
} else if arg_param == "Float.FloatingPoint" {
buf.push_str("Float");
} else {
if write_parens {
buf.push_str("(");
}
// buf.push_str("Num ");
// buf.push_str(&arg_param);
buf.push_str("Num ");
buf.push_str(&arg_param);
// if write_parens {
// buf.push_str(")");
// }
// }
// } else if module_name == "List" && type_name == "List" {
// if write_parens {
// buf.push_str("(");
// }
if write_parens {
buf.push_str(")");
}
}
} else if module_name == "List" && type_name == "List" {
if write_parens {
buf.push_str("(");
}
// buf.push_str("List ");
buf.push_str("List ");
// let arg = args
// .into_iter()
// .next()
// .unwrap_or_else(|| panic!("List did not have any type parameters somehow."));
// let arg_content = subs.get(arg).content;
let arg = args
.into_iter()
.next()
.unwrap_or_else(|| panic!("List did not have any type parameters somehow."));
let arg_content = subs.get(arg).content;
// write_content(arg_content, subs, buf, true);
write_content(arg_content, subs, buf, true);
// if write_parens {
// buf.push_str(")");
// }
// } else {
// if write_parens {
// buf.push_str("(");
// }
if write_parens {
buf.push_str(")");
}
} else {
if write_parens {
buf.push_str("(");
}
// buf.push_str(&format!("{}.{}", module_name, type_name));
buf.push_str(&format!("{}.{}", module_name, type_name));
// for arg in args {
// buf.push_str(" ");
// write_content(subs.get(arg).content, subs, buf, true);
// }
for arg in args {
buf.push_str(" ");
write_content(subs.get(arg).content, subs, buf, true);
}
// if write_parens {
// buf.push_str(")");
// }
// }
// }
if write_parens {
buf.push_str(")");
}
}
}
// fn write_fn(
// args: Vec<Variable>,
// ret: Variable,
// subs: &mut Subs,
// buf: &mut String,
// use_parens: bool,
// ) {
// let mut needs_comma = false;
fn write_fn(
args: Vec<Variable>,
ret: Variable,
subs: &mut Subs,
buf: &mut String,
use_parens: bool,
) {
let mut needs_comma = false;
// if use_parens {
// buf.push_str("(");
// }
if use_parens {
buf.push_str("(");
}
// for arg in args {
// if needs_comma {
// buf.push_str(", ");
// } else {
// needs_comma = true;
// }
for arg in args {
if needs_comma {
buf.push_str(", ");
} else {
needs_comma = true;
}
// write_content(subs.get(arg).content, subs, buf, false);
// }
write_content(subs.get(arg).content, subs, buf, false);
}
// buf.push_str(" -> ");
// write_content(subs.get(ret).content, subs, buf, false);
buf.push_str(" -> ");
write_content(subs.get(ret).content, subs, buf, false);
// if use_parens {
// buf.push_str(")");
// }
// }
if use_parens {
buf.push_str(")");
}
}

View file

@ -4,16 +4,15 @@ use subs::{Content, Descriptor, FlatType, Subs, Variable};
use types::Constraint::{self, *};
use types::Type::{self, *};
type Env<'a> = ImMap<Symbol<'a>, Variable>;
type Env<'a> = ImMap<Symbol, Variable>;
pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Constraint<'a>) {
// println!("\nSolving:\n\n\t{:?}\n\n", constraint);
pub fn solve<'a>(env: &Env<'a>, subs: &mut Subs, constraint: &Constraint) {
match constraint {
True => (),
Eq(typ, expected_type, _region) => {
// TODO use region?
let actual = type_to_variable(subs, typ.clone());
let expected = type_to_variable(subs, expected_type.get_type());
let expected = type_to_variable(subs, expected_type.clone().get_type());
subs.union(actual, expected);
}
@ -23,7 +22,7 @@ pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Const
subs.copy_var(env.get(&symbol).unwrap_or_else(|| {
panic!("Could not find symbol {:?} in env {:?}", symbol, env)
}));
let expected = type_to_variable(subs, expected_type.get_type());
let expected = type_to_variable(subs, expected_type.clone().get_type());
subs.union(actual, expected);
}
@ -32,31 +31,29 @@ pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Const
solve(env, subs, sub_constraint);
}
}
Let(box_let_constraint) => {
let let_con = *box_let_constraint;
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(env, subs, &let_con.assignments_constraint)
}
ret_con => {
ref ret_con => {
// Solve the assignments' constraints first.
solve(env, subs, &let_con.assignments_constraint);
// Add a variable for each assignment to the env.
let mut new_env = env.clone();
for (symbol, loc_type) in let_con.assignment_types {
for (symbol, loc_type) in let_con.assignment_types.iter() {
// We must not overwrite existing symbols! If we do,
// we will overwrite procedure entries, which were
// inserted earlier in solving. (If we allowed
// shadowing, we'd need to do something fancier here.)
if !new_env.contains_key(&symbol) {
let var = type_to_variable(subs, loc_type.value);
let var = type_to_variable(subs, loc_type.value.clone());
new_env.insert(symbol, var);
new_env.insert(symbol.clone(), var);
}
}
@ -71,7 +68,7 @@ pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Const
}
}
fn type_to_variable<'a>(subs: &'a mut Subs<'a>, typ: Type<'a>) -> Variable {
fn type_to_variable<'a>(subs: &'a mut Subs, typ: Type) -> Variable {
match typ {
Variable(var) => var,
Apply {

View file

@ -1,13 +1,11 @@
use bumpalo::Bump;
use ena::unify::{InPlace, UnificationTable, UnifyKey};
use std::fmt;
use types::Problem;
use unify;
#[derive(Debug)]
pub struct Subs<'a> {
pub struct Subs {
utable: UnificationTable<InPlace<Variable>>,
pub arena: &'a Bump,
}
#[derive(Copy, PartialEq, Eq, Clone)]
@ -42,20 +40,19 @@ impl UnifyKey for Variable {
}
}
impl<'a> Subs<'a> {
pub fn new(arena: &'a Bump) -> Self {
impl Subs {
pub fn new() -> Self {
Subs {
utable: UnificationTable::default(),
arena,
}
}
pub fn fresh(&'a mut self, value: Descriptor) -> Variable {
pub fn fresh(&mut self, value: Descriptor) -> Variable {
self.utable.new_key(value)
}
/// Unions two keys without the possibility of failure.
pub fn union(&'a mut self, left: Variable, right: Variable) {
pub fn union(&mut self, left: Variable, right: Variable) {
let l_root = self.utable.get_root_key(left.into());
let r_root = self.utable.get_root_key(right.into());
@ -66,18 +63,18 @@ impl<'a> Subs<'a> {
}
}
pub fn get(&'a mut self, key: Variable) -> Descriptor {
pub fn get(&mut self, key: Variable) -> Descriptor {
self.utable.probe_value(key)
}
pub fn set(&'a mut self, key: Variable, r_value: Descriptor) {
pub fn set(&mut self, key: Variable, r_value: Descriptor) {
let l_key = self.utable.get_root_key(key.into());
let unified = unify::unify_var_val(self, l_key, &r_value);
self.utable.update_value(l_key, |node| node.value = unified);
}
pub fn mk_flex_var(&'a mut self) -> Variable {
pub fn mk_flex_var(&mut self) -> Variable {
self.fresh(flex_var_descriptor())
}

View file

@ -1,4 +1,3 @@
use bumpalo::{self, Bump};
use can::symbol::Symbol;
use collections::ImMap;
use operator::{ArgSide, Operator};
@ -21,29 +20,29 @@ pub const MOD_DEFAULT: &'static str = "Default";
pub const TYPE_NUM: &'static str = "Num";
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Type<'a> {
pub enum Type {
EmptyRec,
/// A function. The types of its arguments, then the type of its return value.
Function(&'a [Type<'a>], &'a Type<'a>),
Operator(&'a OperatorType<'a>),
Function(Vec<Type>, Box<Type>),
Operator(Box<OperatorType>),
/// Applying a type to some arguments (e.g. Map.Map String Int)
Apply {
module_name: &'a str,
name: &'a str,
args: &'a [Type<'a>],
module_name: Box<str>,
name: Box<str>,
args: Vec<Type>,
},
Variable(Variable),
/// A type error, which will code gen to a runtime error
Erroneous(Problem),
}
impl<'a> Type<'a> {
pub fn for_operator(arena: &'a Bump, op: Operator) -> OperatorType<'a> {
impl Type {
pub fn for_operator(op: Operator) -> OperatorType {
use self::Operator::*;
match op {
Slash => op_type(Type::float(arena), Type::float(arena), Type::float(arena)),
DoubleSlash => op_type(Type::int(arena), Type::int(arena), Type::int(arena)),
Slash => op_type(Type::float(), Type::float(), Type::float()),
DoubleSlash => op_type(Type::int(), Type::int(), Type::int()),
// TODO actually, don't put these in types.rs - instead, replace them
// with an equivalence to their corresponding stdlib functions - e.g.
// Slash generates a new variable and an Eq constraint with Float.div.
@ -51,61 +50,61 @@ impl<'a> Type<'a> {
}
}
pub fn num(args: &'a [Type<'a>]) -> Self {
pub fn num(args: Vec<Type>) -> Self {
Type::Apply {
module_name: MOD_NUM,
name: TYPE_NUM,
module_name: MOD_NUM.into(),
name: TYPE_NUM.into(),
args,
}
}
pub fn float(arena: &'a Bump) -> Self {
pub fn float() -> Self {
let floating_point = Type::Apply {
module_name: MOD_FLOAT,
name: "FloatingPoint",
args: &[],
module_name: MOD_FLOAT.into(),
name: "FloatingPoint".into(),
args: Vec::new(),
};
Type::num(bumpalo::vec![in &arena; floating_point].into_bump_slice())
Type::num(vec![floating_point])
}
pub fn int(arena: &'a Bump) -> Self {
pub fn int() -> Self {
let integer = Type::Apply {
module_name: MOD_INT,
name: "Integer",
args: &[],
module_name: MOD_INT.into(),
name: "Integer".into(),
args: Vec::new(),
};
Type::num(bumpalo::vec![in &arena; integer].into_bump_slice())
Type::num(vec![integer])
}
pub fn string() -> Self {
Type::Apply {
module_name: MOD_STR,
name: "Str",
args: &[],
module_name: MOD_STR.into(),
name: "Str".into(),
args: Vec::new(),
}
}
/// This is needed to constrain `if` conditionals
pub fn bool() -> Self {
Type::Apply {
module_name: MOD_DEFAULT,
name: "Bool",
args: &[],
module_name: MOD_DEFAULT.into(),
name: "Bool".into(),
args: Vec::new(),
}
}
}
fn op_type<'a>(left: Type<'a>, right: Type<'a>, ret: Type<'a>) -> OperatorType<'a> {
fn op_type(left: Type, right: Type, ret: Type) -> OperatorType {
OperatorType { left, right, ret }
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct OperatorType<'a> {
pub left: Type<'a>,
pub right: Type<'a>,
pub ret: Type<'a>,
pub struct OperatorType {
pub left: Type,
pub right: Type,
pub ret: Type,
}
#[derive(Debug, Clone)]
@ -138,21 +137,21 @@ pub enum Reason {
}
#[derive(Debug, Clone)]
pub enum Constraint<'a> {
Eq(Type<'a>, Expected<Type<'a>>, Region),
Lookup(Symbol<'a>, Expected<Type<'a>>, Region),
pub enum Constraint {
Eq(Type, Expected<Type>, Region),
Lookup(Symbol, Expected<Type>, Region),
True, // Used for things that always unify, e.g. blanks and runtime errors
Let(&'a LetConstraint<'a>),
And(&'a [Constraint<'a>]),
Let(Box<LetConstraint>),
And(Vec<Constraint>),
}
#[derive(Debug, Clone)]
pub struct LetConstraint<'a> {
pub rigid_vars: &'a [Variable],
pub flex_vars: &'a [Variable],
pub assignment_types: ImMap<Symbol<'a>, Located<Type<'a>>>,
pub assignments_constraint: Constraint<'a>,
pub ret_constraint: Constraint<'a>,
pub struct LetConstraint {
pub rigid_vars: Vec<Variable>,
pub flex_vars: Vec<Variable>,
pub assignment_types: ImMap<Symbol, Located<Type>>,
pub assignments_constraint: Constraint,
pub ret_constraint: Constraint,
}
#[derive(PartialEq, Eq, Debug, Clone)]

View file

@ -3,11 +3,7 @@ use subs::{Descriptor, FlatType, Subs, Variable};
use types::Problem;
#[inline(always)]
pub fn unify_vars<'a>(
subs: &'a mut Subs<'a>,
left_key: Variable,
right_key: Variable,
) -> Descriptor {
pub fn unify_vars<'a>(subs: &'a mut Subs, left_key: Variable, right_key: Variable) -> Descriptor {
let right = subs.get(right_key);
unify_var_val(subs, left_key, &right)
@ -15,7 +11,7 @@ pub fn unify_vars<'a>(
#[inline(always)]
pub fn unify_var_val<'a>(
subs: &'a mut Subs<'a>,
subs: &'a mut Subs,
left_key: Variable,
right: &'a Descriptor,
) -> Descriptor {
@ -24,11 +20,7 @@ pub fn unify_var_val<'a>(
unify(subs, &left, right)
}
pub fn unify<'a>(
subs: &'a mut Subs<'a>,
left: &'a Descriptor,
right: &'a Descriptor,
) -> Descriptor {
pub fn unify<'a>(subs: &'a mut Subs, left: &'a Descriptor, right: &'a Descriptor) -> Descriptor {
let answer = match left.content {
FlexVar(ref opt_name) => unify_flex(opt_name, &right.content),
RigidVar(ref name) => unify_rigid(name, &right.content),
@ -44,7 +36,7 @@ pub fn unify<'a>(
#[inline(always)]
fn unify_structure<'a>(
subs: &'a mut Subs<'a>,
subs: &'a mut Subs,
flat_type: &'a FlatType,
other: &'a Content,
) -> Descriptor {
@ -69,11 +61,7 @@ fn unify_structure<'a>(
}
#[inline(always)]
fn unify_flat_type<'a>(
subs: &'a mut Subs<'a>,
left: &'a FlatType,
right: &'a FlatType,
) -> Descriptor {
fn unify_flat_type<'a>(subs: &'a mut Subs, left: &'a FlatType, right: &'a FlatType) -> Descriptor {
use subs::FlatType::*;
match (left, right) {
@ -124,7 +112,7 @@ fn unify_flat_type<'a>(
}
}
fn unify_args<'a, I>(subs: &'a mut Subs<'a>, left_iter: I, right_iter: I) -> Vec<Variable>
fn unify_args<'a, I>(subs: &'a mut Subs, left_iter: I, right_iter: I) -> Vec<Variable>
where
I: Iterator<Item = &'a Variable>,
{
@ -144,7 +132,7 @@ where
answer
}
fn union_vars<'a>(subs: &'a mut Subs<'a>, l_var: Variable, r_var: Variable) -> Variable {
fn union_vars<'a>(subs: &'a mut Subs, l_var: Variable, r_var: Variable) -> Variable {
// Look up the descriptors we have for these variables, and unify them.
let descriptor = unify_vars(subs, l_var.clone(), r_var.clone());