mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Start merging constraint gen into canonicalization
This commit is contained in:
parent
4704799df5
commit
ae301f3c86
19 changed files with 989 additions and 829 deletions
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
733
src/can/mod.rs
733
src/can/mod.rs
File diff suppressed because it is too large
Load diff
|
@ -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
217
src/can/precedence.rs
Normal 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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
250
src/constrain.rs
250
src/constrain.rs
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
}
|
||||
|
|
23
src/solve.rs
23
src/solve.rs
|
@ -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 {
|
||||
|
|
19
src/subs.rs
19
src/subs.rs
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
91
src/types.rs
91
src/types.rs
|
@ -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)]
|
||||
|
|
26
src/unify.rs
26
src/unify.rs
|
@ -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());
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue