mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Adding assignments to solver
This commit is contained in:
parent
44bdcdb3f3
commit
49cd2ede8a
4 changed files with 153 additions and 72 deletions
83
src/solve.rs
83
src/solve.rs
|
@ -14,10 +14,13 @@
|
||||||
// }
|
// }
|
||||||
|
|
||||||
use subs::{Subs, Variable, Descriptor, Content, FlatType};
|
use subs::{Subs, Variable, Descriptor, Content, FlatType};
|
||||||
|
use collections::ImMap;
|
||||||
use types::Constraint::{self, *};
|
use types::Constraint::{self, *};
|
||||||
use types::Type::{self, *};
|
use types::Type::{self, *};
|
||||||
|
|
||||||
pub fn solve(subs: &mut Subs, constraint: Constraint) {
|
type Env = ImMap<String, Variable>;
|
||||||
|
|
||||||
|
pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
|
||||||
println!("\nSolving:\n\n\t{:?}\n\n", constraint);
|
println!("\nSolving:\n\n\t{:?}\n\n", constraint);
|
||||||
match constraint {
|
match constraint {
|
||||||
True => (),
|
True => (),
|
||||||
|
@ -29,63 +32,55 @@ pub fn solve(subs: &mut Subs, constraint: Constraint) {
|
||||||
},
|
},
|
||||||
And(sub_constraints) => {
|
And(sub_constraints) => {
|
||||||
for sub_constraint in sub_constraints {
|
for sub_constraint in sub_constraints {
|
||||||
solve(subs, sub_constraint);
|
solve(env, subs, sub_constraint);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Let(box_let_constraint) => {
|
Let(box_let_constraint) => {
|
||||||
let let_con = *box_let_constraint;
|
let let_con = *box_let_constraint;
|
||||||
|
let no_rigid_vars = let_con.rigid_vars.is_empty();
|
||||||
|
|
||||||
// CLet [] flexs _ headerCon CTrue ->
|
match let_con.ret_constraint {
|
||||||
// do introduce rank pools flexs
|
True if no_rigid_vars => {
|
||||||
// solve env rank pools state headerCon
|
// If the return expression is guaranteed to solve,
|
||||||
|
// and there are no rigid vars to worry about,
|
||||||
|
// solve the assignments themselves and move on.
|
||||||
|
solve(env, subs, let_con.assignments_constraint)
|
||||||
|
},
|
||||||
|
body_con => {
|
||||||
|
if no_rigid_vars && let_con.flex_vars.is_empty() {
|
||||||
|
// Solve the assignments' constraints first.
|
||||||
|
solve(env, subs, let_con.assignments_constraint);
|
||||||
|
|
||||||
// CLet [] [] header headerCon subCon ->
|
// Add a variable for each assignment to the env.
|
||||||
// do state1 <- solve env rank pools state headerCon
|
let new_env = env.clone();
|
||||||
// locals <- traverse (A.traverse (typeToVariable rank pools)) header
|
|
||||||
// let newEnv = Map.union env (Map.map A.toValue locals)
|
|
||||||
// state2 <- solve newEnv rank pools state1 subCon
|
|
||||||
// foldM occurs state2 $ Map.toList locals
|
|
||||||
|
|
||||||
// CLet rigids flexs header headerCon subCon ->
|
for (name, loc_type) in let_con.assignment_types {
|
||||||
// do
|
let var = type_to_variable(subs, loc_type.value);
|
||||||
// -- work in the next pool to localize header
|
|
||||||
// let nextRank = rank + 1
|
|
||||||
// let poolsLength = MVector.length pools
|
|
||||||
// nextPools <-
|
|
||||||
// if nextRank < poolsLength
|
|
||||||
// then return pools
|
|
||||||
// else MVector.grow pools poolsLength
|
|
||||||
|
|
||||||
// -- introduce variables
|
new_env.insert(name, var);
|
||||||
// let vars = rigids ++ flexs
|
}
|
||||||
// forM_ vars $ \var ->
|
|
||||||
// UF.modify var $ \(Descriptor content _ mark copy) ->
|
|
||||||
// Descriptor content nextRank mark copy
|
|
||||||
// MVector.write nextPools nextRank vars
|
|
||||||
|
|
||||||
// -- run solver in next pool
|
// Now solve the body, using the new env which includes
|
||||||
// locals <- traverse (A.traverse (typeToVariable nextRank nextPools)) header
|
// the assignments' name-to-variable mappings.
|
||||||
// (State savedEnv mark errors) <-
|
solve(&new_env, subs, let_con.ret_constraint);
|
||||||
// solve env nextRank nextPools state headerCon
|
|
||||||
|
|
||||||
// let youngMark = mark
|
// TODO do an occurs check for each of the assignments!
|
||||||
// let visitMark = nextMark youngMark
|
} else {
|
||||||
// let finalMark = nextMark visitMark
|
let vars = let_con.rigid_vars;
|
||||||
|
|
||||||
// -- pop pool
|
vars.extend(let_con.flex_vars);
|
||||||
// generalize youngMark visitMark nextRank nextPools
|
|
||||||
// MVector.write nextPools nextRank []
|
|
||||||
|
|
||||||
// -- check that things went well
|
// Add a variable for each assignment to the env.
|
||||||
// mapM_ isGeneric rigids
|
let new_env = env.clone();
|
||||||
|
|
||||||
// let newEnv = Map.union env (Map.map A.toValue locals)
|
|
||||||
// let tempState = State savedEnv finalMark errors
|
|
||||||
// newState <- solve newEnv rank nextPools tempState subCon
|
|
||||||
|
|
||||||
// foldM occurs newState (Map.toList locals)
|
|
||||||
|
|
||||||
|
for (name, loc_type) in let_con.assignment_types {
|
||||||
|
let var = type_to_variable(subs, loc_type.value);
|
||||||
|
|
||||||
|
new_env.insert(name, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@ use subs::Variable;
|
||||||
use region::Region;
|
use region::Region;
|
||||||
use operator::Operator;
|
use operator::Operator;
|
||||||
use region::Located;
|
use region::Located;
|
||||||
use canonicalize::Symbol;
|
use collections::ImMap;
|
||||||
use collections::MutMap;
|
|
||||||
|
|
||||||
type ModuleName = String;
|
type ModuleName = String;
|
||||||
|
|
||||||
|
@ -56,9 +55,9 @@ pub enum Constraint {
|
||||||
pub struct LetConstraint {
|
pub struct LetConstraint {
|
||||||
pub rigid_vars: Vec<Variable>,
|
pub rigid_vars: Vec<Variable>,
|
||||||
pub flex_vars: Vec<Variable>,
|
pub flex_vars: Vec<Variable>,
|
||||||
pub header: MutMap<Symbol, Located<Type>>,
|
pub assignment_types: ImMap<String, Located<Type>>,
|
||||||
pub header_constraint: Constraint,
|
pub assignments_constraint: Constraint,
|
||||||
pub body_constraint: Constraint,
|
pub ret_constraint: Constraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::ops::{Add, Sub, Mul, Neg};
|
||||||
|
|
||||||
/// Approx is stored as an f64 under the hood.
|
/// Approx is stored as an f64 under the hood.
|
||||||
///
|
///
|
||||||
|
@ -89,15 +90,19 @@ impl<T> Approximation<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<f64> for Approximation<T> {
|
impl<Valid> From<f64> for Approximation<Valid> {
|
||||||
fn from(num: f64) -> Self {
|
fn from(num: f64) -> Self {
|
||||||
|
if num.is_finite() {
|
||||||
Approximation { value: num, phantom: PhantomData }
|
Approximation { value: num, phantom: PhantomData }
|
||||||
|
} else {
|
||||||
|
panic!("Tried to convert {:?} to Float.", num)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<f32> for Approximation<T> {
|
impl<Valid> From<f32> for Approximation<Valid> {
|
||||||
fn from(num: f32) -> Self {
|
fn from(num: f32) -> Self {
|
||||||
Approximation { value: num as f64, phantom: PhantomData }
|
(num as f64).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +112,39 @@ impl<T> Into<f64> for Approximation<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Into<f32> for Approximation<T> {
|
impl Add for Approximation<Valid> {
|
||||||
fn into(self) -> f32 {
|
type Output = Approximation<Valid>;
|
||||||
self.value as f32
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
assert_no_overflow(self.value.add(other.value), "addition")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Approximation<Valid> {
|
||||||
|
type Output = Approximation<Valid>;
|
||||||
|
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
assert_no_overflow(self.value.mul(other.value), "multiplication")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Approximation<Valid> {
|
||||||
|
type Output = Approximation<Valid>;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
assert_no_overflow(self.value.sub(other.value), "subtraction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn assert_no_overflow(num: f64, op: &'static str) -> Approximation<Valid> {
|
||||||
|
if num.is_finite() {
|
||||||
|
Approximation { value: num, phantom: PhantomData }
|
||||||
|
} else if num.is_infinity() {
|
||||||
|
panic!("Float overflowed during ", op)
|
||||||
|
} else if num.is_negative_infinity() {
|
||||||
|
panic!("Float underflowed during ", op)
|
||||||
|
} else {
|
||||||
|
panic!("Float was NaN during ", op)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -68,22 +68,22 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_empty_record() {
|
fn empty_record() {
|
||||||
infer_eq("{}", "{}");
|
infer_eq("{}", "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_int() {
|
fn int_literal() {
|
||||||
infer_eq("5", "Num.Num *");
|
infer_eq("5", "Num.Num *");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_fractional() {
|
fn fractional_literal() {
|
||||||
infer_eq("0.5", "Num.Num (Num.Fractional *)");
|
infer_eq("0.5", "Num.Num (Num.Fractional *)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_string() {
|
fn string_literal() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
"type inference!"
|
"type inference!"
|
||||||
|
@ -93,7 +93,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_empty_string() {
|
fn empty_string() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
""
|
""
|
||||||
|
@ -107,7 +107,7 @@ mod test_infer {
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_empty_list() {
|
fn empty_list() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[]
|
[]
|
||||||
|
@ -117,7 +117,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_list_of_lists() {
|
fn list_of_lists() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[[]]
|
[[]]
|
||||||
|
@ -127,7 +127,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_triple_nested_list() {
|
fn triple_nested_list() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[[[]]]
|
[[[]]]
|
||||||
|
@ -137,7 +137,17 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_list_of_one_num() {
|
fn nested_empty_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(r#"
|
||||||
|
[ [], [ [] ] ]
|
||||||
|
"#),
|
||||||
|
"List.List (List.List (List.List *))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_of_one_num() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[42]
|
[42]
|
||||||
|
@ -147,7 +157,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_triple_nested_num_list() {
|
fn triple_nested_num_list() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[[[ 5 ]]]
|
[[[ 5 ]]]
|
||||||
|
@ -157,7 +167,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_list_of_nums() {
|
fn list_of_nums() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[ 1, 2, 3 ]
|
[ 1, 2, 3 ]
|
||||||
|
@ -167,7 +177,17 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_list_of_one_string() {
|
fn nested_list_of_nums() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(r#"
|
||||||
|
[ [ 1 ], [ 2, 3 ] ]
|
||||||
|
"#),
|
||||||
|
"List.List (List.List (Num.Num *))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_of_one_string() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[ "cowabunga" ]
|
[ "cowabunga" ]
|
||||||
|
@ -177,7 +197,7 @@ mod test_infer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_triple_nested_string_list() {
|
fn triple_nested_string_list() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[[[ "foo" ]]]
|
[[[ "foo" ]]]
|
||||||
|
@ -186,9 +206,8 @@ mod test_infer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_list_of_strings() {
|
fn list_of_strings() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(r#"
|
indoc!(r#"
|
||||||
[ "foo", "bar" ]
|
[ "foo", "bar" ]
|
||||||
|
@ -197,6 +216,38 @@ mod test_infer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LIST MISMATCH
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mismatch_heterogeneous_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(r#"
|
||||||
|
[ "foo", 5 ]
|
||||||
|
"#),
|
||||||
|
"List.List <type mismatch>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mismatch_heterogeneous_nested_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(r#"
|
||||||
|
[ [ "foo", 5 ] ]
|
||||||
|
"#),
|
||||||
|
"List.List (List.List <type mismatch>)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mismatch_heterogeneous_nested_empty_list() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(r#"
|
||||||
|
[ [ 1 ], [ [] ] ]
|
||||||
|
"#),
|
||||||
|
"List.List (List.List <type mismatch>)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn infer_interpolated_string() {
|
// fn infer_interpolated_string() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue