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 collections::ImMap;
|
||||
use types::Constraint::{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);
|
||||
match constraint {
|
||||
True => (),
|
||||
|
@ -29,63 +32,55 @@ pub fn solve(subs: &mut Subs, constraint: Constraint) {
|
|||
},
|
||||
And(sub_constraints) => {
|
||||
for sub_constraint in sub_constraints {
|
||||
solve(subs, sub_constraint);
|
||||
solve(env, subs, sub_constraint);
|
||||
}
|
||||
},
|
||||
Let(box_let_constraint) => {
|
||||
let let_con = *box_let_constraint;
|
||||
let no_rigid_vars = let_con.rigid_vars.is_empty();
|
||||
|
||||
// CLet [] flexs _ headerCon CTrue ->
|
||||
// do introduce rank pools flexs
|
||||
// solve env rank pools state headerCon
|
||||
match let_con.ret_constraint {
|
||||
True if no_rigid_vars => {
|
||||
// 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 ->
|
||||
// do state1 <- solve env rank pools state headerCon
|
||||
// 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
|
||||
// Add a variable for each assignment to the env.
|
||||
let new_env = env.clone();
|
||||
|
||||
// CLet rigids flexs header headerCon subCon ->
|
||||
// do
|
||||
// -- 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
|
||||
for (name, loc_type) in let_con.assignment_types {
|
||||
let var = type_to_variable(subs, loc_type.value);
|
||||
|
||||
// -- introduce variables
|
||||
// let vars = rigids ++ flexs
|
||||
// forM_ vars $ \var ->
|
||||
// UF.modify var $ \(Descriptor content _ mark copy) ->
|
||||
// Descriptor content nextRank mark copy
|
||||
// MVector.write nextPools nextRank vars
|
||||
new_env.insert(name, var);
|
||||
}
|
||||
|
||||
// -- run solver in next pool
|
||||
// locals <- traverse (A.traverse (typeToVariable nextRank nextPools)) header
|
||||
// (State savedEnv mark errors) <-
|
||||
// solve env nextRank nextPools state headerCon
|
||||
// Now solve the body, using the new env which includes
|
||||
// the assignments' name-to-variable mappings.
|
||||
solve(&new_env, subs, let_con.ret_constraint);
|
||||
|
||||
// let youngMark = mark
|
||||
// let visitMark = nextMark youngMark
|
||||
// let finalMark = nextMark visitMark
|
||||
// TODO do an occurs check for each of the assignments!
|
||||
} else {
|
||||
let vars = let_con.rigid_vars;
|
||||
|
||||
// -- pop pool
|
||||
// generalize youngMark visitMark nextRank nextPools
|
||||
// MVector.write nextPools nextRank []
|
||||
vars.extend(let_con.flex_vars);
|
||||
|
||||
// -- check that things went well
|
||||
// mapM_ isGeneric rigids
|
||||
|
||||
// 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)
|
||||
// Add a variable for each assignment to the env.
|
||||
let new_env = env.clone();
|
||||
|
||||
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 operator::Operator;
|
||||
use region::Located;
|
||||
use canonicalize::Symbol;
|
||||
use collections::MutMap;
|
||||
use collections::ImMap;
|
||||
|
||||
type ModuleName = String;
|
||||
|
||||
|
@ -56,9 +55,9 @@ pub enum Constraint {
|
|||
pub struct LetConstraint {
|
||||
pub rigid_vars: Vec<Variable>,
|
||||
pub flex_vars: Vec<Variable>,
|
||||
pub header: MutMap<Symbol, Located<Type>>,
|
||||
pub header_constraint: Constraint,
|
||||
pub body_constraint: Constraint,
|
||||
pub assignment_types: ImMap<String, Located<Type>>,
|
||||
pub assignments_constraint: Constraint,
|
||||
pub ret_constraint: Constraint,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, Sub, Mul, Neg};
|
||||
|
||||
/// 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 {
|
||||
if num.is_finite() {
|
||||
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 {
|
||||
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> {
|
||||
fn into(self) -> f32 {
|
||||
self.value as f32
|
||||
impl Add for Approximation<Valid> {
|
||||
type Output = Approximation<Valid>;
|
||||
|
||||
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]
|
||||
fn infer_empty_record() {
|
||||
fn empty_record() {
|
||||
infer_eq("{}", "{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_int() {
|
||||
fn int_literal() {
|
||||
infer_eq("5", "Num.Num *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_fractional() {
|
||||
fn fractional_literal() {
|
||||
infer_eq("0.5", "Num.Num (Num.Fractional *)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_string() {
|
||||
fn string_literal() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
"type inference!"
|
||||
|
@ -93,7 +93,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_empty_string() {
|
||||
fn empty_string() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
""
|
||||
|
@ -107,7 +107,7 @@ mod test_infer {
|
|||
|
||||
|
||||
#[test]
|
||||
fn infer_empty_list() {
|
||||
fn empty_list() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[]
|
||||
|
@ -117,7 +117,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_list_of_lists() {
|
||||
fn list_of_lists() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[[]]
|
||||
|
@ -127,7 +127,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_triple_nested_list() {
|
||||
fn triple_nested_list() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[[[]]]
|
||||
|
@ -137,7 +137,17 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[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(
|
||||
indoc!(r#"
|
||||
[42]
|
||||
|
@ -147,7 +157,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_triple_nested_num_list() {
|
||||
fn triple_nested_num_list() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[[[ 5 ]]]
|
||||
|
@ -157,7 +167,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_list_of_nums() {
|
||||
fn list_of_nums() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[ 1, 2, 3 ]
|
||||
|
@ -167,7 +177,17 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[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(
|
||||
indoc!(r#"
|
||||
[ "cowabunga" ]
|
||||
|
@ -177,7 +197,7 @@ mod test_infer {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_triple_nested_string_list() {
|
||||
fn triple_nested_string_list() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[[[ "foo" ]]]
|
||||
|
@ -186,9 +206,8 @@ mod test_infer {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn infer_list_of_strings() {
|
||||
fn list_of_strings() {
|
||||
infer_eq(
|
||||
indoc!(r#"
|
||||
[ "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]
|
||||
// fn infer_interpolated_string() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue