mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Got things compiling
This commit is contained in:
parent
3b72951846
commit
e05230519e
5 changed files with 307 additions and 234 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -17,12 +17,14 @@ version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ena"
|
name = "dogged"
|
||||||
version = "0.11.0"
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
|
@ -37,6 +39,20 @@ name = "maplit"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordermap"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "petgraph"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty_assertions"
|
name = "pretty_assertions"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -50,8 +66,10 @@ dependencies = [
|
||||||
name = "roc"
|
name = "roc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dogged 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -78,9 +96,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||||
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||||
"checksum ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f56c93cc076508c549d9bb747f79aa9b4eb098be7b8cad8830c3137ef52d1e00"
|
"checksum dogged 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2638df109789fe360f0d9998c5438dd19a36678aaf845e46f285b688b1a1657a"
|
||||||
|
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||||
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
|
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
|
||||||
|
"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
|
||||||
|
"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
|
||||||
"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
|
"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
|
||||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
|
@ -4,8 +4,10 @@ version = "0.1.0"
|
||||||
authors = ["Richard Feldman <richard.t.feldman@gmail.com>"]
|
authors = ["Richard Feldman <richard.t.feldman@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ena = "0.11.0"
|
dogged = { version = "0.2.0", optional = true }
|
||||||
|
log = "0.4"
|
||||||
|
petgraph = { version = "0.4.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions="0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
Copyright 2019 Richard Feldman
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -5,5 +5,11 @@
|
||||||
// pub mod repl;
|
// pub mod repl;
|
||||||
|
|
||||||
pub mod solve;
|
pub mod solve;
|
||||||
|
mod ena;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[cfg(feature = "persistent")]
|
||||||
|
extern crate dogged;
|
||||||
|
|
||||||
extern crate ena;
|
|
||||||
|
|
484
src/solve.rs
484
src/solve.rs
|
@ -1,41 +1,36 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use self::VarContent::*;
|
use self::Variable::*;
|
||||||
use self::Operator::*;
|
use ena::unify::{UnificationTable, UnifyKey, InPlace};
|
||||||
use ena::unify::UnificationTable;
|
|
||||||
use ena::unify::UnifyValue;
|
|
||||||
use ena::unify::InPlace;
|
|
||||||
|
|
||||||
pub type Name<'a> = &'a str;
|
pub type Name = String;
|
||||||
|
|
||||||
pub type ModuleName<'a> = &'a str;
|
pub type ModuleName = String;
|
||||||
|
|
||||||
type UTable<'a> = UnificationTable<InPlace<Variable<'a>>>;
|
type UTable = UnificationTable<InPlace<VarId>>;
|
||||||
|
|
||||||
type TypeUnion<'a> = BTreeSet<Type<'a>>;
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
type VarUnion<'a> = BTreeSet<VarContent<'a>>;
|
pub enum Type {
|
||||||
|
// Symbol(String),
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
// Int,
|
||||||
pub enum Type<'a> {
|
// Float,
|
||||||
Symbol(&'a str),
|
// Number,
|
||||||
Int,
|
// TypeUnion(BTreeSet<Type>),
|
||||||
Float,
|
// Function(Box<Type>, Box<Type>),
|
||||||
Number,
|
CallOperator(Operator, Box<Type>, Box<Type>),
|
||||||
Function(Box<Type<'a>>, Box<Type<'a>>),
|
|
||||||
CallOperator(Operator, Box<&'a Type<'a>>, Box<&'a Type<'a>>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Expr<'a> {
|
pub enum Expr {
|
||||||
HexOctalBinary(i64), // : Int
|
HexOctalBinary(i64), // : Int
|
||||||
FractionalNumber(f64), // : Float
|
FractionalNumber(f64), // : Float
|
||||||
WholeNumber(i64), // : Int | Float
|
WholeNumber(i64), // : Int | Float
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
CallOperator(Operator, Box<&'a Expr<'a>>, Box<&'a Expr<'a>>),
|
CallOperator(Operator, Box<Expr>, Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Operator {
|
pub enum Operator {
|
||||||
Plus, Minus, FloatDivision, IntDivision,
|
Plus, Minus, FloatDivision, IntDivision,
|
||||||
}
|
}
|
||||||
|
@ -45,176 +40,232 @@ pub enum Problem {
|
||||||
Mismatch
|
Mismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Variable<'a> {
|
pub enum Variable {
|
||||||
content: VarContent<'a>,
|
|
||||||
rank: u8
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
enum VarContent<'a> {
|
|
||||||
Wildcard,
|
Wildcard,
|
||||||
RigidVar(&'a Name<'a>),
|
RigidVar(Name),
|
||||||
FlexUnion(TypeUnion<'a>),
|
FlexUnion(BTreeSet<VarId>),
|
||||||
RigidUnion(TypeUnion<'a>),
|
RigidUnion(BTreeSet<VarId>),
|
||||||
Structure(FlatType<'a>),
|
Structure(FlatType),
|
||||||
Mismatch
|
Mismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_rigid<'a>(named: &'a VarContent<'a>, other: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
match other {
|
|
||||||
Wildcard => named,
|
|
||||||
RigidVar(_) => Mismatch,
|
|
||||||
FlexUnion(_) => Mismatch,
|
|
||||||
RigidUnion(_) => Mismatch,
|
|
||||||
Mismatch => other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_rigid_union<'a>(rigid_union: &'a VarUnion<'a>, var: &'a VarContent<'a>, other: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
match other {
|
|
||||||
Wildcard => var,
|
|
||||||
RigidVar(_) => Mismatch,
|
|
||||||
FlexUnion(flex_union) => {
|
|
||||||
// a flex union can conform to a rigid one, as long as
|
|
||||||
// as the rigid union contains all the flex union's options
|
|
||||||
if rigid_union.is_subset(flex_union) {
|
|
||||||
var
|
|
||||||
} else {
|
|
||||||
Mismatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RigidUnion(_) => Mismatch,
|
|
||||||
Mismatch => other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_flex_union<'a>(flex_union: &'a VarUnion<'a>, var: &'a VarContent<'a>, other: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
match other {
|
|
||||||
Wildcard => var,
|
|
||||||
RigidVar(_) => Mismatch,
|
|
||||||
RigidUnion(rigid_union) => {
|
|
||||||
// a flex union can conform to a rigid one, as long as
|
|
||||||
// as the rigid union contains all the flex union's options
|
|
||||||
if rigid_union.is_subset(flex_union) {
|
|
||||||
other
|
|
||||||
} else {
|
|
||||||
Mismatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FlexUnion(other_union) => unify_flex_unions(flex_union, var, other_union, other),
|
|
||||||
Structure(flat_type) => unify_flex_union_with_flat_type(flex_union, flat_type),
|
|
||||||
Mismatch => other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_flex_unions<'a>(my_union: &'a VarUnion<'a>, my_var: &'a VarContent<'a>, other_union: &'a VarUnion<'a>, other_var: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
// Prioritize not allocating a new BTreeSet if possible.
|
|
||||||
if my_union == other_union {
|
|
||||||
return my_var;
|
|
||||||
}
|
|
||||||
|
|
||||||
let types_in_common = my_union.intersection(other_union);
|
|
||||||
|
|
||||||
if types_in_common.is_empty() {
|
|
||||||
Mismatch
|
|
||||||
} else {
|
|
||||||
let unified_union: VarUnion<'a> = types_in_common.into_iter().collect();
|
|
||||||
|
|
||||||
FlexUnion(unified_union)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn actually_unify<'a>(first: &'a VarContent<'a>, second: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
match first {
|
|
||||||
// wildcard types defer to whatever the other type happens to be.
|
|
||||||
Wildcard => second,
|
|
||||||
FlexUnion(union) => unify_flex_union(union, first, second),
|
|
||||||
RigidVar(Name) => unify_rigid(first, second),
|
|
||||||
RigidUnion(union) => unify_rigid_union(union, first, second),
|
|
||||||
Structure(flat_type) => unify_structure(flat_type, first, second),
|
|
||||||
// Mismatches propagate.
|
|
||||||
Mismatch => first
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CanonicalModuleName = String;
|
type CanonicalModuleName = String;
|
||||||
|
|
||||||
enum FlatType<'a> {
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
Function(Variable<'a>, Variable<'a>),
|
pub enum FlatType {
|
||||||
// Apply a higher-kinded type constructor by name
|
Function(VarId, VarId),
|
||||||
// e.g. apply `Array` to the variable `Int` to form `Array Int`
|
|
||||||
// ApplyTypeConstructor(CanonicalModuleName, Name, &'a Variable<'a>)
|
// Apply a higher-kinded type constructor by name. For example:
|
||||||
Tuple2(Variable<'a>, Variable<'a>),
|
// "Apply the higher-kinded type constructor `Array` to the variable `Int`
|
||||||
// Tuple3(Variable<'a>, Variable<'a>, Variable<'a>),
|
// to form `Array Int`."
|
||||||
// TupleN(Vec<Variable<'a>>), // Last resort - allocates
|
// ApplyTypeConstructor(CanonicalModuleName, Name, VarId)
|
||||||
// Record1 (Map.Map N.Name Variable) Variable,
|
Tuple2(VarId, VarId),
|
||||||
|
Tuple3(VarId, VarId, VarId),
|
||||||
|
// TupleN(Vec<VarId>), // Last resort - allocates
|
||||||
|
// Record1 (Map.Map N.Name VarId) VarId,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_args<'a>(arg1: &'a Variable<'a>, arg2: Variable) -> Result<Vec<Variable<'a>>, Vec<Variable<'a>>> {
|
#[inline]
|
||||||
guarded_unify(arg1, arg2)
|
fn unify_rigid(named: &Variable, other: &Variable) -> Variable {
|
||||||
// case subUnify arg1 arg2 of
|
|
||||||
// Unify k ->
|
|
||||||
// k vars
|
|
||||||
// (\vs () -> unifyArgs vs context others1 others2 ok err)
|
|
||||||
// (\vs () -> unifyArgs vs context others1 others2 err err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn guarded_unify<'a>(utable: UTable<'a>, left: Variable<'a>, right: Variable<'a>) -> Result<(), ()> {
|
|
||||||
if utable.unioned(left, right) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let left_descriptor = utable.probe_key(left);
|
|
||||||
let right_descriptor = utable.probe_key(right);
|
|
||||||
|
|
||||||
actually_unify(left, left_descriptor, right, right_descriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unify_structure<'a>(utable: &'a mut UTable<'a>, flat_type: &'a FlatType<'a>, var: &'a VarContent<'a>, other: &'a VarContent<'a>) -> &'a VarContent<'a> {
|
|
||||||
match other {
|
match other {
|
||||||
Wildcard => var,
|
Wildcard => named.clone(),
|
||||||
RigidVar(_) => Mismatch,
|
RigidVar(_) => Mismatch,
|
||||||
FlexUnion(union) => unify_flex_union_with_flat_type(flex_union, flat_type),
|
FlexUnion(_) => Mismatch,
|
||||||
RigidUnion(_) => Mismatch,
|
RigidUnion(_) => Mismatch,
|
||||||
Structure(other_flat_type) =>
|
Structure(_) => { panic!("TODO"); Mismatch }
|
||||||
match (flat_type, other) {
|
Mismatch => other.clone()
|
||||||
(FlatType::Function(my_arg, my_return),
|
|
||||||
FlatType::Function(other_arg, other_return)) => {
|
|
||||||
guarded_unify(utable, my_arg, other_arg);
|
|
||||||
guarded_unify(utable, my_returned, other_returned);
|
|
||||||
},
|
|
||||||
(FlatType::Tuple2(my_first, my_second),
|
|
||||||
FlatType::Tuple2(other_first, other_second)) => {
|
|
||||||
guarded_unify(utable, my_first, other_first);
|
|
||||||
guarded_unify(utable, my_second, other_second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mismatch =>
|
|
||||||
other
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_flex_union_with_flat_type<'a>(utable: &'a mut UTable<'a>, flex_union: &'a VarUnion<'a>, flat_type: &'a FlatType<'a>) -> &'a VarContent<'a> {
|
#[inline]
|
||||||
if var_union_contains(flex_union, flat_type) {
|
fn unify_rigid_union(utable: &mut UTable, rigid_union: &BTreeSet<VarId>, var: &Variable, other: &Variable) -> Variable {
|
||||||
// This will use the UnifyValue trait to unify the values.
|
match other {
|
||||||
utable.union(var1, var2);
|
Wildcard => var.clone(),
|
||||||
|
RigidVar(_) => Mismatch,
|
||||||
|
FlexUnion(flex_union) => {
|
||||||
|
if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) {
|
||||||
|
var.clone()
|
||||||
} else {
|
} else {
|
||||||
Mismatch
|
Mismatch
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Structure(_) => { panic!("TODO"); Mismatch }
|
||||||
|
RigidUnion(_) => Mismatch,
|
||||||
|
Mismatch => other.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rigid_union_fits_flex_union(utable: &mut UTable, rigid_union: &BTreeSet<VarId>, flex_union: &BTreeSet<VarId>) -> bool {
|
||||||
|
if rigid_union.is_subset(&flex_union) {
|
||||||
|
// If the keys of the rigid one are a subset of the flex keys, we're done.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
type ExpectedType<'a> = Type<'a>;
|
let potentially_missing_flex_ids = flex_union.difference(rigid_union);
|
||||||
|
|
||||||
pub enum Constraint<'a> {
|
// a flex union can conform to a rigid one, as long
|
||||||
|
// as the rigid union contains all the flex union's alternative types
|
||||||
|
let rigid_union_values: BTreeSet<Variable> =
|
||||||
|
rigid_union.iter().map(|var_id| utable.probe_value(*var_id)).collect();
|
||||||
|
|
||||||
|
for flex_var_id in potentially_missing_flex_ids {
|
||||||
|
let flex_val = utable.probe_value(*flex_var_id);
|
||||||
|
|
||||||
|
if !rigid_union_values.contains(&flex_val) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unify_flex_union(utable: &mut UTable, flex_union: &BTreeSet<VarId>, var: &Variable, other: &Variable) -> Variable {
|
||||||
|
match other {
|
||||||
|
Wildcard => var.clone(),
|
||||||
|
RigidVar(_) => Mismatch,
|
||||||
|
RigidUnion(rigid_union) => {
|
||||||
|
if rigid_union_fits_flex_union(utable, &rigid_union, &flex_union) {
|
||||||
|
other.clone()
|
||||||
|
} else {
|
||||||
|
Mismatch
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FlexUnion(other_union) => unify_flex_unions(&flex_union, &other_union),
|
||||||
|
Structure(_) => unify_flex_union_with_structure(&flex_union, other),
|
||||||
|
Mismatch => other.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unify_flex_unions(my_union: &BTreeSet<VarId>, other_union: &BTreeSet<VarId>) -> Variable {
|
||||||
|
let ids_in_common = my_union.intersection(other_union);
|
||||||
|
let unified_union: BTreeSet<VarId> = ids_in_common.into_iter().map(|var_id| *var_id).collect();
|
||||||
|
|
||||||
|
// If they have no types in common, that's a mismatch.
|
||||||
|
if unified_union.len() == 0 {
|
||||||
|
Mismatch
|
||||||
|
} else {
|
||||||
|
FlexUnion(unified_union)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unify_vars(utable: &mut UTable, first: &Variable, second: &Variable) -> Variable {
|
||||||
|
match first {
|
||||||
|
// wildcard types defer to whatever the other type happens to be.
|
||||||
|
Wildcard => second.clone(),
|
||||||
|
FlexUnion(union) => unify_flex_union(utable, &union, first, second),
|
||||||
|
RigidVar(Name) => unify_rigid(first, second),
|
||||||
|
RigidUnion(union) => unify_rigid_union(utable, &union, first, second),
|
||||||
|
Structure(flat_type) => unify_structure(utable, flat_type, first, second),
|
||||||
|
// Mismatches propagate.
|
||||||
|
Mismatch => first.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unify_structure(utable: &mut UTable, flat_type: &FlatType, var: &Variable, other: &Variable) -> Variable {
|
||||||
|
match other {
|
||||||
|
Wildcard => var.clone(),
|
||||||
|
RigidVar(_) => Mismatch,
|
||||||
|
FlexUnion(flex_union) => unify_flex_union_with_structure(&flex_union, var),
|
||||||
|
RigidUnion(_) => Mismatch,
|
||||||
|
Structure(other_flat_type) => unify_flat_types(utable, flat_type, other_flat_type),
|
||||||
|
Mismatch => other.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unify_flat_types(utable: &mut UTable, flat_type: &FlatType, other_flat_type: &FlatType) -> Variable {
|
||||||
|
match (flat_type, other_flat_type) {
|
||||||
|
(FlatType::Function(my_arg, my_return),
|
||||||
|
FlatType::Function(other_arg, other_return)) => {
|
||||||
|
let new_arg = unify_var_ids(utable, *my_arg, *other_arg);
|
||||||
|
let new_return = unify_var_ids(utable, *my_return, *other_return);
|
||||||
|
|
||||||
|
// Propagate any mismatches.
|
||||||
|
if new_arg == Mismatch {
|
||||||
|
new_arg
|
||||||
|
} else if new_return == Mismatch {
|
||||||
|
new_return
|
||||||
|
} else {
|
||||||
|
let new_arg_id = utable.new_key(new_arg);
|
||||||
|
let new_return_id = utable.new_key(new_return);
|
||||||
|
|
||||||
|
Structure(FlatType::Function(new_arg_id, new_return_id))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(FlatType::Function(_, __return), _) => Mismatch,
|
||||||
|
(_, FlatType::Function(_, __return)) => Mismatch,
|
||||||
|
(FlatType::Tuple2(my_first, my_second),
|
||||||
|
FlatType::Tuple2(other_first, other_second)) => {
|
||||||
|
let new_first = unify_var_ids(utable, *my_first, *other_first);
|
||||||
|
let new_second = unify_var_ids(utable, *my_second, *other_second);
|
||||||
|
|
||||||
|
// Propagate any mismatches.
|
||||||
|
if new_first == Mismatch {
|
||||||
|
new_first
|
||||||
|
} else if new_second == Mismatch {
|
||||||
|
new_second
|
||||||
|
} else {
|
||||||
|
let new_first_id = utable.new_key(new_first);
|
||||||
|
let new_second_id = utable.new_key(new_second);
|
||||||
|
|
||||||
|
Structure(FlatType::Tuple2(new_first_id, new_second_id))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(FlatType::Tuple2(_, _), _) => Mismatch,
|
||||||
|
(_, FlatType::Tuple2(_, _)) => Mismatch,
|
||||||
|
(FlatType::Tuple3(my_first, my_second, my_third),
|
||||||
|
FlatType::Tuple3(other_first, other_second, other_third)) => {
|
||||||
|
let new_first = unify_var_ids(utable, *my_first, *other_first);
|
||||||
|
let new_second = unify_var_ids(utable, *my_second, *other_second);
|
||||||
|
let new_third = unify_var_ids(utable, *my_third, *other_third);
|
||||||
|
|
||||||
|
// Propagate any mismatches.
|
||||||
|
if new_first == Mismatch {
|
||||||
|
new_first
|
||||||
|
} else if new_second == Mismatch {
|
||||||
|
new_second
|
||||||
|
} else if new_third == Mismatch {
|
||||||
|
new_third
|
||||||
|
} else {
|
||||||
|
let new_first_id = utable.new_key(new_first);
|
||||||
|
let new_second_id = utable.new_key(new_second);
|
||||||
|
let new_third_id = utable.new_key(new_third);
|
||||||
|
|
||||||
|
Structure(FlatType::Tuple3(new_first_id, new_second_id, new_third_id))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// (FlatType::Tuple3(_, _, _), _) => Mismatch,
|
||||||
|
// (_, FlatType::Tuple3(_, _, _)) => Mismatch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unify_flex_union_with_structure(flex_union: &BTreeSet<VarId>, var: &Variable) -> Variable {
|
||||||
|
// TODO I guess iterate through the set, looking up Variables
|
||||||
|
|
||||||
|
panic!("TODO");
|
||||||
|
// if flex_union.contains(var) {
|
||||||
|
// Narrow the union to the one member type
|
||||||
|
var.clone()
|
||||||
|
// } else {
|
||||||
|
// Mismatch
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExpectedType = Type;
|
||||||
|
|
||||||
|
pub enum Constraint {
|
||||||
True,
|
True,
|
||||||
Equal(Type<'a>, ExpectedType<'a>),
|
Equal(Type, ExpectedType),
|
||||||
Batch(Vec<Constraint<'a>>),
|
Batch(Vec<Constraint>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer_type<'a>(expr: Expr<'a>) -> Result<Type<'a>, Problem> {
|
pub fn infer_type(expr: Expr) -> Result<Type, Problem> {
|
||||||
Err(Problem::Mismatch)
|
Err(Problem::Mismatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,75 +273,63 @@ struct State {
|
||||||
errors: Vec<String>
|
errors: Vec<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given a type, create a constraint variable for it and add it to the table.
|
||||||
impl<'a> UnifyValue for Variable<'a> {
|
// Return the VarId corresponding to the variable in the table.
|
||||||
// We return our own Mismatch variant to track errors.
|
fn type_to_var_id(utable: &mut UTable, typ: Type) -> VarId {
|
||||||
type Error = ena::unify::NoError;
|
|
||||||
|
|
||||||
fn unify_values(value1: &'a Variable<'a>, value2: &'a Variable<'a>) -> Result<Variable<'a>, ena::unify::NoError> {
|
|
||||||
// TODO unify 'em
|
|
||||||
|
|
||||||
// TODO problem: Elm's unification mutates and looks things up as it goes.
|
|
||||||
// I can see these possible ways to proceed:
|
|
||||||
// (1) Try to have the table's values contain a mutable reference to the table itself.
|
|
||||||
// This sounds like a mistake.
|
|
||||||
// (2) Implement unification without mutating as we go.
|
|
||||||
// Might be too slow, and might not even work.
|
|
||||||
// Like, what if I need to look something up in the middle?
|
|
||||||
// (3) Make a custom fork of ena that supports Elm's way.
|
|
||||||
// (3a) Change the unify_values function to accept the table itself, so it can be
|
|
||||||
// passed in and used during unification
|
|
||||||
// (3b) Change the unify_values function to accept the table itself, so it can be
|
|
||||||
// passed in and used during unification. I'm not super confident this would work.
|
|
||||||
//
|
|
||||||
// Possibly before doing any of this, I should look at ena's examples/tests
|
|
||||||
|
|
||||||
// TODO also I'm pretty sure in this implementation,
|
|
||||||
// I'm supposed to let them take care of the rank.
|
|
||||||
Ok(Variable {content, rank: min(rank1, rank2)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn type_to_var(rank: u8, typ: Type) -> Variable {
|
|
||||||
match typ {
|
match typ {
|
||||||
Type::CallOperator(op, left_type, right_type) => {
|
Type::CallOperator(op, box left_type, box right_type) => {
|
||||||
let left_var = type_to_var(left_type);
|
let left_var_id = type_to_var_id(utable, left_type);
|
||||||
let right_var = type_to_var(right_type);
|
let right_var_id = type_to_var_id(utable, right_type);
|
||||||
|
|
||||||
// TODO should we match on op to hardcode the types we expect?
|
// TODO should we match on op to hardcode the types we expect?
|
||||||
let flat_type = FlatType::Function(left_var, right_var);
|
let flat_type = FlatType::Function(left_var_id, right_var_id);
|
||||||
let content = Structure(flat_type);
|
|
||||||
|
|
||||||
utable.new_key(Variable {rank, content})
|
utable.new_key(Structure(flat_type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct VarId(u32);
|
||||||
|
|
||||||
pub fn unify(utable: Table, left_var: Variable, right_var: Variable) -> Result<(), ()>{
|
impl UnifyKey for VarId {
|
||||||
let left_content = utable.probe_value(left_var);
|
type Value = Variable;
|
||||||
let right_content = utable.probe_value(right_var);
|
|
||||||
|
fn index(&self) -> u32 { self.0 }
|
||||||
|
fn from_index(u: u32) -> VarId { VarId(u) }
|
||||||
|
|
||||||
|
// tag is a static string that's only used in debugging
|
||||||
|
fn tag() -> &'static str { "VarId" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unify_var_ids(utable: &mut UTable, left_id: VarId, right_id: VarId) -> Variable {
|
||||||
|
let left_content = utable.probe_value(left_id);
|
||||||
|
let right_content = utable.probe_value(right_id);
|
||||||
|
|
||||||
if left_content == right_content {
|
if left_content == right_content {
|
||||||
Ok(())
|
left_content
|
||||||
} else {
|
} else {
|
||||||
Ok(actually_unify(left, left_desc, right, right_desc))
|
unify_vars(utable, &left_content, &right_content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(rank: u8, state: State, constraint: Constraint) {
|
type TypeError = String;
|
||||||
|
|
||||||
|
pub fn solve(utable: &mut UTable, errors: &mut Vec<TypeError>, constraint: Constraint) {
|
||||||
match constraint {
|
match constraint {
|
||||||
True =>
|
Constraint::True => {},
|
||||||
state
|
|
||||||
|
|
||||||
Equal(actual_type, expectation) => {
|
Constraint::Equal(actual_type, expectation) => {
|
||||||
let actual_var = type_to_var(rank, actual_type)
|
let actual_var_id = type_to_var_id(utable, actual_type);
|
||||||
let expected_var = type_to_var(rank, expectation)
|
let expected_var_id = type_to_var_id(utable, expectation);
|
||||||
let answer = unify(actual_var, expected_var)
|
let answer = unify_var_ids(utable, actual_var_id, expected_var_id);
|
||||||
|
|
||||||
match answer {
|
panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)");
|
||||||
Ok vars ->
|
()
|
||||||
panic!("TODO abc");
|
// match answer {
|
||||||
|
// Mismatch => {
|
||||||
|
// panic!("Oh no! TYPE MISMATCH! (TODO: record errors as appropriate)");
|
||||||
|
// }
|
||||||
// do introduce rank pools vars
|
// do introduce rank pools vars
|
||||||
// return state
|
// return state
|
||||||
|
|
||||||
|
@ -304,7 +343,12 @@ pub fn solve(rank: u8, state: State, constraint: Constraint) {
|
||||||
// return $ addError state $
|
// return $ addError state $
|
||||||
// Error.BadExpr region category actualType $
|
// Error.BadExpr region category actualType $
|
||||||
// Error.typeReplace expectation expectedType
|
// Error.typeReplace expectation expectedType
|
||||||
}
|
// }
|
||||||
|
},
|
||||||
|
|
||||||
|
Constraint::Batch(_) => {
|
||||||
|
panic!("TODO");
|
||||||
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue