Merge pull request #2 from rtfeldman/fix-identity

Generate type var names when pretty printing
This commit is contained in:
Richard Feldman 2019-11-16 22:38:27 -05:00 committed by GitHub
commit 1e8423a368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 470 additions and 255 deletions

View file

@ -4,6 +4,7 @@ use self::num::{
finish_parsing_bin, finish_parsing_float, finish_parsing_hex, finish_parsing_int,
finish_parsing_oct, float_expr_from_result, int_expr_from_result,
};
use self::pattern::PatternState;
use self::pattern::PatternType::*;
use self::pattern::{canonicalize_pattern, Pattern};
use self::problem::Problem;
@ -235,7 +236,9 @@ fn canonicalize_expr(
let fn_region = loc_fn.region;
let fn_expected = NoExpectation(fn_type.clone());
// TODO look up the name and use NamedFnArg if possible.
let fn_reason = Reason::AnonymousFnCall(loc_args.len() as u8);
let fn_reason = Reason::AnonymousFnCall {
arity: loc_args.len() as u8,
};
// Canonicalize the function expression and its arguments
let (fn_expr, mut output) = canonicalize_expr(
@ -269,7 +272,9 @@ fn canonicalize_expr(
let arg_var = subs.mk_flex_var();
let arg_type = Variable(arg_var);
// TODO look up the name and use NamedFnArg if possible.
let reason = Reason::AnonymousFnArg(index as u8);
let reason = Reason::AnonymousFnArg {
arg_index: index as u8,
};
let expected_arg = ForReason(reason, arg_type.clone(), region);
let (arg_expr, arg_out) = canonicalize_expr(
rigids,
@ -471,12 +476,13 @@ fn canonicalize_expr(
scope.idents = union_pairs(scope.idents, arg_idents.iter());
let mut state = PatternState {
assignment_types: ImMap::default(),
headers: ImMap::default(),
vars: Vec::with_capacity(loc_arg_patterns.len()),
constraints: Vec::with_capacity(1),
};
let args = constrain_args(loc_arg_patterns.iter(), &scope, subs, &mut state);
let mut can_args: Vec<Located<Pattern>> = Vec::with_capacity(loc_arg_patterns.len());
let mut vars = Vec::with_capacity(state.vars.capacity());
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
for loc_pattern in loc_arg_patterns.into_iter() {
// Exclude the current ident from shadowable_idents; you can't shadow yourself!
@ -485,10 +491,14 @@ fn canonicalize_expr(
remove_idents(&loc_pattern.value, &mut shadowable_idents);
let pattern_var = subs.mk_flex_var();
let pattern_expected = PExpected::NoExpectation(Type::Variable(pattern_var));
let pattern_type = Type::Variable(pattern_var);
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
let (can_arg, _state) = canonicalize_pattern(
pattern_types.push(pattern_type);
let can_arg = canonicalize_pattern(
env,
&mut state,
subs,
&mut scope,
FunctionArg,
@ -498,10 +508,19 @@ fn canonicalize_expr(
pattern_expected,
);
vars.push(pattern_var);
can_args.push(can_arg);
}
let body_type = NoExpectation(args.ret_type);
let ret_var = subs.mk_flex_var();
let ret_type = Type::Variable(ret_var);
state.vars.push(ret_var);
let fn_typ = Type::Function(pattern_types, Box::new(ret_type.clone()));
let body_type = NoExpectation(ret_type);
let (loc_body_expr, mut output) = canonicalize_expr(
rigids,
env,
@ -523,17 +542,17 @@ fn canonicalize_expr(
let typ = Variable(var);
output.constraint = exists(
args.vars,
state.vars.clone(),
And(vec![
Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: state.vars,
assignment_types: state.assignment_types,
assignment_types: state.headers,
assignments_constraint,
ret_constraint,
})),
// "the closure's type is equal to the var we've stored for later use in the proc"
Eq(args.typ, NoExpectation(typ.clone()), region),
Eq(fn_typ, NoExpectation(typ.clone()), region),
// "the var we've stored for later is equal to the overall expected type"
Eq(typ, expected, region),
]),
@ -566,7 +585,7 @@ fn canonicalize_expr(
region,
output.references.clone(),
var,
args.ret_var,
ret_var,
);
// Always return a function pointer, in case that's how the closure is being used (e.g. with Apply).
@ -847,8 +866,15 @@ fn canonicalize_case_branch<'a>(
}
}
let (loc_can_pattern, state) = canonicalize_pattern(
let mut state = PatternState {
headers: ImMap::default(),
vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1),
};
let loc_can_pattern = canonicalize_pattern(
env,
&mut state,
subs,
&mut scope,
CaseBranch,
@ -1258,48 +1284,6 @@ impl Info {
/// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary.
// type BoundTypeVars = ImMap<Box<str>, Type>;
struct PatternState {
assignment_types: ImMap<Symbol, Located<Type>>,
vars: Vec<Variable>,
constraints: Vec<Constraint>,
}
impl PatternState {
pub fn add_pattern(
&mut self,
scope: &Scope,
loc_pattern: Located<ast::Pattern>,
expected: Expected<Type>,
) {
let region = loc_pattern.region;
match loc_pattern.value {
ast::Pattern::Identifier(name) => {
let symbol = scope.symbol(&name);
self.add_to_assignment_types(region, symbol, expected)
}
ast::Pattern::Underscore => (),
_ => panic!("TODO other patterns"),
}
}
fn add_to_assignment_types(
&mut self,
region: Region,
symbol: Symbol,
expected: Expected<Type>,
) {
self.assignment_types.insert(
symbol,
Located {
region,
value: expected.get_type(),
},
);
}
}
fn add_pattern_to_lookup_types<'a>(
scope: &Scope,
loc_pattern: Located<ast::Pattern<'a>>,
@ -1367,6 +1351,10 @@ fn can_defs<'a>(
let iter = defs.iter();
for loc_def in iter {
// Make types for the body expr, even if we won't end up having a body.
let expr_var = subs.mk_flex_var();
let expr_type = Type::Variable(expr_var);
// Each assignment gets to have all the idents in scope that are assigned in this
// block. Order of assignments doesn't matter, thanks to referential transparency!
let (opt_loc_pattern, (loc_can_expr, can_output)) = match loc_def.value {
@ -1400,37 +1388,6 @@ fn can_defs<'a>(
(None, (loc_expr, Output::new(True)))
}
Def::Body(ref loc_pattern, loc_expr) => {
// Make types for the pattern and the body expr.
let expr_var = subs.mk_flex_var();
let expr_type = Type::Variable(expr_var);
let pattern_var = subs.mk_flex_var();
let pattern_type = Type::Variable(pattern_var);
flex_info.vars.push(pattern_var);
let mut state = PatternState {
assignment_types: ImMap::default(),
vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1),
};
state.add_pattern(
&scope,
loc_pattern.clone(),
NoExpectation(pattern_type.clone()),
);
let def_constraint = And(state.constraints);
// Any time there's a lookup on this symbol in the outer Let,
// it should result in this expression's type. After all, this
// is the type to which this symbol is assigned!
add_pattern_to_lookup_types(
&scope,
loc_pattern.clone(),
&mut flex_info.assignment_types,
expr_type.clone(),
);
let (loc_can_expr, output) = canonicalize_expr(
rigids,
env,
@ -1438,17 +1395,9 @@ fn can_defs<'a>(
&mut scope,
loc_expr.region,
&loc_expr.value,
NoExpectation(expr_type),
NoExpectation(expr_type.clone()),
);
flex_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: state.vars,
assignment_types: state.assignment_types,
assignments_constraint: def_constraint,
ret_constraint: output.constraint.clone(),
})));
(Some(loc_pattern), (loc_can_expr, output))
}
Def::CustomType(_, _) => {
@ -1470,10 +1419,18 @@ fn can_defs<'a>(
remove_idents(&loc_pattern.value, &mut shadowable_idents);
let pattern_var = subs.mk_flex_var();
let pattern_expected = PExpected::NoExpectation(Type::Variable(pattern_var));
let pattern_type = Type::Variable(pattern_var);
let pattern_expected = PExpected::NoExpectation(pattern_type);
let (loc_can_pattern, _state) = canonicalize_pattern(
let mut state = PatternState {
headers: ImMap::default(),
vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1),
};
let loc_can_pattern = canonicalize_pattern(
env,
&mut state,
subs,
&mut scope,
Assignment,
@ -1482,6 +1439,28 @@ fn can_defs<'a>(
&mut shadowable_idents,
pattern_expected,
);
flex_info.vars.push(pattern_var);
// Any time there's a lookup on this symbol in the outer Let,
// it should result in this expression's type. After all, this
// is the type to which this symbol is assigned!
add_pattern_to_lookup_types(
&scope,
// TODO can we we avoid this clone?
loc_pattern.clone(),
&mut flex_info.assignment_types,
expr_type.clone(),
);
flex_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: state.vars,
assignment_types: state.headers,
assignments_constraint: And(state.constraints),
ret_constraint: can_output.constraint.clone(),
})));
let mut renamed_closure_assignment: Option<&Symbol> = None;
// Give closures names (and tail-recursive status) where appropriate.
@ -1679,7 +1658,12 @@ fn can_defs<'a>(
// As a bonus, the topological sort also reveals any cycles between the assignments, allowing
// us to give a CircularAssignment error.
let successors = |symbol: &Symbol| -> ImSet<Symbol> {
let (_, references) = refs_by_assignment.get(symbol).unwrap();
let (_, references) = refs_by_assignment.get(symbol).unwrap_or_else(|| {
panic!(
"Could not find symbol {:?} in refs_by_assignment, which had: {:?}",
symbol, refs_by_assignment
)
});
local_successors(&references, &env.procedures)
};
@ -1745,60 +1729,3 @@ fn can_defs<'a>(
}
}
}
struct Args {
pub vars: Vec<Variable>,
pub typ: Type,
pub ret_type: Type,
pub ret_var: Variable,
}
fn constrain_args<'a, I>(args: I, scope: &Scope, subs: &mut Subs, state: &mut PatternState) -> Args
where
I: Iterator<Item = &'a Located<ast::Pattern<'a>>>,
{
let (mut vars, arg_types) = patterns_to_variables(args, scope, subs, state);
let ret_var = subs.mk_flex_var();
let ret_type = Type::Variable(ret_var);
vars.push(ret_var);
let typ = Type::Function(arg_types, Box::new(ret_type.clone()));
Args {
vars,
typ,
ret_type,
ret_var,
}
}
fn patterns_to_variables<'a, I>(
patterns: I,
scope: &Scope,
subs: &mut Subs,
state: &mut PatternState,
) -> (Vec<Variable>, Vec<Type>)
where
I: Iterator<Item = &'a Located<ast::Pattern<'a>>>,
{
let mut vars = Vec::with_capacity(state.vars.capacity());
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
for loc_pattern in patterns {
let pattern_var = subs.mk_flex_var();
let pattern_type = Type::Variable(pattern_var);
state.add_pattern(
scope,
loc_pattern.clone(),
NoExpectation(pattern_type.clone()),
);
vars.push(pattern_var);
pattern_types.push(pattern_type);
}
(vars, pattern_types)
}

View file

@ -47,6 +47,7 @@ pub enum PatternType {
pub fn canonicalize_pattern<'a>(
env: &'a mut Env,
state: &'a mut PatternState,
subs: &mut Subs,
scope: &mut Scope,
pattern_type: PatternType,
@ -54,7 +55,7 @@ pub fn canonicalize_pattern<'a>(
region: Region,
shadowable_idents: &'a mut ImMap<Ident, (Symbol, Region)>,
expected: PExpected<Type>,
) -> (Located<Pattern>, State) {
) -> Located<Pattern> {
use self::PatternType::*;
use can::ast::Pattern::*;
@ -245,6 +246,7 @@ pub fn canonicalize_pattern<'a>(
&SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => {
return canonicalize_pattern(
env,
state,
subs,
scope,
pattern_type,
@ -257,21 +259,12 @@ pub fn canonicalize_pattern<'a>(
_ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern),
};
let mut state = State {
headers: ImMap::default(),
vars: Vec::new(),
constraints: Vec::new(),
};
add_constraints(&pattern, &scope, region, expected, state);
add_constraints(&pattern, region, expected, &mut state);
(
Located {
region,
value: can_pattern,
},
state,
)
Located {
region,
value: can_pattern,
}
}
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
@ -284,7 +277,7 @@ fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region)
// CONSTRAIN
pub struct State {
pub struct PatternState {
pub headers: ImMap<Symbol, Located<Type>>,
pub vars: Vec<Variable>,
pub constraints: Vec<Constraint>,
@ -292,9 +285,10 @@ pub struct State {
fn add_constraints<'a>(
pattern: &'a ast::Pattern<'a>,
scope: &'a Scope,
region: Region,
expected: PExpected<Type>,
state: &'a mut State,
state: &'a mut PatternState,
) {
use parse::ast::Pattern::*;
@ -304,7 +298,7 @@ fn add_constraints<'a>(
}
Identifier(name) => {
state.headers.insert(
Symbol::new("TODO pass home into add_constraints, or improve Symbol to not need it for idents in patterns", name),
scope.symbol(name),
Located {
region,
value: expected.get_type(),
@ -339,7 +333,7 @@ fn add_constraints<'a>(
}
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => {
add_constraints(pattern, region, expected, state)
add_constraints(pattern, scope, region, expected, state)
}
Variant(_, _) | Apply(_, _) | RecordDestructure(_) | EmptyRecordLiteral => {

View file

@ -37,8 +37,13 @@ pub enum UndoLog<D: SnapshotVecDelegate> {
Other(D::Undo),
}
/// A Vec where we have Debug overridden to render the indices like
/// a hashmap, since we really care about those when debugging one of these.
#[derive(Clone)]
struct BackingVec<T>(Vec<T>);
pub struct SnapshotVec<D: SnapshotVecDelegate> {
values: Vec<D::Value>,
values: BackingVec<D::Value>,
undo_log: Vec<UndoLog<D>>,
num_open_snapshots: usize,
}
@ -78,13 +83,32 @@ pub trait SnapshotVecDelegate {
impl<D: SnapshotVecDelegate> Default for SnapshotVec<D> {
fn default() -> Self {
SnapshotVec {
values: Vec::new(),
values: BackingVec(Vec::new()),
undo_log: Vec::new(),
num_open_snapshots: 0,
}
}
}
impl<T> fmt::Debug for BackingVec<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{{{")?;
for (index, elem) in self.0.iter().enumerate() {
write!(f, "\n {} => {:?},", index, elem)?;
}
if !self.0.is_empty() {
write!(f, "\n")?;
}
write!(f, "}}}}")
}
}
impl<D: SnapshotVecDelegate> SnapshotVec<D> {
pub fn new() -> Self {
Self::default()
@ -92,7 +116,7 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
pub fn with_capacity(c: usize) -> SnapshotVec<D> {
SnapshotVec {
values: Vec::with_capacity(c),
values: BackingVec(Vec::with_capacity(c)),
undo_log: Vec::new(),
num_open_snapshots: 0,
}
@ -109,16 +133,16 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
}
pub fn len(&self) -> usize {
self.values.len()
self.values.0.len()
}
pub fn is_empty(&self) -> bool {
self.values.len() == 0
self.values.0.len() == 0
}
pub fn push(&mut self, elem: D::Value) -> usize {
let len = self.values.len();
self.values.push(elem);
let len = self.values.0.len();
self.values.0.push(elem);
if self.in_snapshot() {
self.undo_log.push(NewElem(len));
@ -128,26 +152,26 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
}
pub fn get(&self, index: usize) -> &D::Value {
&self.values[index]
&self.values.0[index]
}
/// Reserve space for new values, just like an ordinary vec.
pub fn reserve(&mut self, additional: usize) {
// This is not affected by snapshots or anything.
self.values.reserve(additional);
self.values.0.reserve(additional);
}
/// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone
/// automatically, so you should be sure call `record()` with some sort of suitable undo
/// action.
pub fn get_mut(&mut self, index: usize) -> &mut D::Value {
&mut self.values[index]
&mut self.values.0[index]
}
/// Updates the element at the given index. The old value will saved (and perhaps restored) if
/// a snapshot is active.
pub fn set(&mut self, index: usize, new_elem: D::Value) {
let old_elem = mem::replace(&mut self.values[index], new_elem);
let old_elem = mem::replace(&mut self.values.0[index], new_elem);
if self.in_snapshot() {
self.undo_log.push(SetElem(index, old_elem));
}
@ -157,11 +181,11 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
/// otherwise equivalent to -- invoking `set` for each element.
pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) {
if !self.in_snapshot() {
for (index, slot) in self.values.iter_mut().enumerate() {
for (index, slot) in self.values.0.iter_mut().enumerate() {
*slot = new_elems(index);
}
} else {
for i in 0..self.values.len() {
for i in 0..self.values.0.len() {
self.set(i, new_elems(i));
}
}
@ -173,16 +197,16 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
D::Value: Clone,
{
if self.in_snapshot() {
let old_elem = self.values[index].clone();
let old_elem = self.values.0[index].clone();
self.undo_log.push(SetElem(index, old_elem));
}
op(&mut self.values[index]);
op(&mut self.values.0[index]);
}
pub fn start_snapshot(&mut self) -> Snapshot {
self.num_open_snapshots += 1;
Snapshot {
value_count: self.values.len(),
value_count: self.values.0.len(),
undo_len: self.undo_log.len(),
}
}
@ -205,16 +229,16 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
while self.undo_log.len() > snapshot.undo_len {
match self.undo_log.pop().unwrap() {
NewElem(i) => {
self.values.pop();
assert!(self.values.len() == i);
self.values.0.pop();
assert!(self.values.0.len() == i);
}
SetElem(i, v) => {
self.values[i] = v;
self.values.0[i] = v;
}
Other(u) => {
D::reverse(&mut self.values, u);
D::reverse(&mut self.values.0, u);
}
}
}
@ -244,13 +268,13 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
impl<D: SnapshotVecDelegate> ops::Deref for SnapshotVec<D> {
type Target = [D::Value];
fn deref(&self) -> &[D::Value] {
&*self.values
&*self.values.0
}
}
impl<D: SnapshotVecDelegate> ops::DerefMut for SnapshotVec<D> {
fn deref_mut(&mut self) -> &mut [D::Value] {
&mut *self.values
&mut *self.values.0
}
}
@ -272,9 +296,9 @@ impl<D: SnapshotVecDelegate> Extend<D::Value> for SnapshotVec<D> {
where
T: IntoIterator<Item = D::Value>,
{
let initial_len = self.values.len();
self.values.extend(iterable);
let final_len = self.values.len();
let initial_len = self.values.0.len();
self.values.0.extend(iterable);
let final_len = self.values.0.len();
if self.in_snapshot() {
self.undo_log.extend((initial_len..final_len).map(NewElem));

View file

@ -1,30 +1,192 @@
use collections::{MutMap, MutSet};
use subs::{Content, FlatType, Subs, Variable};
use types;
static WILDCARD: &str = "*";
static EMPTY_RECORD: &str = "{}";
static THE_LETTER_A: u32 = 'a' as u32;
/// Rerquirements for parentheses.
///
/// If we're inside a function (that is, this is either an argument or a return
/// value), we may need to use parens. Examples:
///
/// a -> (* -> a)
/// (* -> a) -> a
///
/// Separately, if we're inside a type parameter, we may need to use parens:
///
/// List Int
/// List (List Int)
///
/// Otherwise, parens are unnecessary.
#[derive(Clone, Copy, Debug, PartialEq)]
enum Parens {
InFn,
InTypeParam,
Unnecessary,
}
/// How many times a root variable appeared in Subs.
///
/// We only care about whether it was a single time or multiple times,
/// because single appearances get a wildcard (*) and multiple times
/// get a generated letter ("a" etc).
enum Appearances {
Single,
Multiple,
}
/// Generate names for all type variables, replacing FlexVar(None) with
/// FlexVar(Some(name)) where appropriate. Example: for the identity
/// function, generate a name of "a" for both its argument and return
/// type variables.
///
/// It's important that we track insertion order, which is why
/// names_needed is a Vec. We also want to count how many times a root
/// appears, because we should only generate a name for it if it appears
/// more than once.
fn find_names_needed(
variable: Variable,
subs: &mut Subs,
roots: &mut Vec<Variable>,
root_appearances: &mut MutMap<Variable, Appearances>,
names_taken: &mut MutSet<String>,
) {
use subs::Content::*;
use subs::FlatType::*;
match subs.get(variable).content {
FlexVar(None) => {
let root = subs.get_root_key(variable);
// If this var is *not* its own root, then the
// root var necessarily appears in multiple places.
// We need a name for it!
match root_appearances.get(&root) {
Some(Appearances::Single) => {
root_appearances.insert(root, Appearances::Multiple);
}
Some(Appearances::Multiple) => {
// It's already multiple, so do nothing!
}
None => {
roots.push(root);
root_appearances.insert(root, Appearances::Single);
}
}
}
FlexVar(Some(_)) => {
// This root already has a name. Nothing to do here!
}
Structure(Apply {
module_name: _,
name: _,
args,
}) => {
for var in args {
find_names_needed(var, subs, roots, root_appearances, names_taken);
}
}
Structure(Func(arg_vars, ret_var)) => {
for var in arg_vars {
find_names_needed(var, subs, roots, root_appearances, names_taken);
}
find_names_needed(ret_var, subs, roots, root_appearances, names_taken);
}
RigidVar(name) => {
// User-defined names are already taken.
// We must not accidentally generate names that collide with them!
names_taken.insert(name.to_string());
}
Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => {
// Errors and empty records don't need names.
}
}
}
pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) {
let mut roots = Vec::new();
let mut letters_used = 0;
let mut appearances = MutMap::default();
let mut taken = MutSet::default();
// Populate names_needed
find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken);
for root in roots {
match appearances.get(&root) {
Some(Appearances::Multiple) => {
letters_used = name_root(letters_used, root, subs, &taken);
}
_ => (),
}
}
}
fn name_root(letters_used: u32, root: Variable, subs: &mut Subs, taken: &MutSet<String>) -> u32 {
// TODO we should arena-allocate this String,
// so all the strings in the entire pass only require ~1 allocation.
let generated_name = if letters_used < 26 {
// This should generate "a", then "b", etc.
std::char::from_u32(THE_LETTER_A + letters_used)
.unwrap_or_else(|| panic!("Tried to convert {} to a char", THE_LETTER_A + letters_used))
.to_string()
} else {
panic!("TODO generate aa, ab, ac, ...");
};
if taken.contains(&generated_name) {
// If the generated name is already taken, try again.
name_root(letters_used + 1, root, subs, taken)
} else {
set_root_name(root, &generated_name, subs);
letters_used + 1
}
}
fn set_root_name(root: Variable, name: &str, subs: &mut Subs) {
use subs::Content::*;
let mut descriptor = subs.get(root);
match descriptor.content {
FlexVar(None) => {
descriptor.content = FlexVar(Some(name.into()));
// TODO is this necessary, or was mutating descriptor in place sufficient?
subs.set(root, descriptor);
}
FlexVar(Some(_existing)) => {
panic!("TODO FIXME - make sure the generated name does not clash with any bound vars! In other words, if the user decided to name a type variable 'a', make sure we don't generate 'a' to name a different one!");
}
_ => (),
}
}
pub fn content_to_string(content: Content, subs: &mut Subs) -> String {
let mut buf = String::new();
write_content(content, subs, &mut buf, false);
write_content(content, subs, &mut buf, Parens::Unnecessary);
buf
}
fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) {
fn write_content(content: Content, subs: &mut Subs, buf: &mut String, parens: Parens) {
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),
Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens),
Error(_) => buf.push_str("<type mismatch>"),
}
}
fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) {
fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, parens: Parens) {
use subs::FlatType::*;
match flat_type {
@ -38,11 +200,10 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_p
args,
subs,
buf,
use_parens,
parens,
),
EmptyRecord => buf.push_str(EMPTY_RECORD),
Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
BinOp(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
Func(args, ret) => write_fn(args, ret, subs, buf, parens),
Erroneous(problem) => {
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
}
@ -55,9 +216,9 @@ fn write_apply(
args: Vec<Variable>,
subs: &mut Subs,
buf: &mut String,
use_parens: bool,
parens: Parens,
) {
let write_parens = use_parens && !args.is_empty();
let write_parens = parens == Parens::InTypeParam && !args.is_empty();
// Hardcoded type aliases
if module_name == "Str" && type_name == "Str" {
@ -70,7 +231,7 @@ fn write_apply(
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, Parens::InTypeParam);
if arg_param == "Int.Integer" {
buf.push_str("Int");
@ -101,7 +262,7 @@ fn write_apply(
.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, Parens::InTypeParam);
if write_parens {
buf.push_str(")");
@ -115,7 +276,7 @@ fn write_apply(
for arg in args {
buf.push_str(" ");
write_content(subs.get(arg).content, subs, buf, true);
write_content(subs.get(arg).content, subs, buf, Parens::InTypeParam);
}
if write_parens {
@ -124,14 +285,9 @@ fn write_apply(
}
}
fn write_fn(
args: Vec<Variable>,
ret: Variable,
subs: &mut Subs,
buf: &mut String,
use_parens: bool,
) {
fn write_fn(args: Vec<Variable>, ret: Variable, subs: &mut Subs, buf: &mut String, parens: Parens) {
let mut needs_comma = false;
let use_parens = parens != Parens::Unnecessary;
if use_parens {
buf.push_str("(");
@ -144,11 +300,11 @@ fn write_fn(
needs_comma = true;
}
write_content(subs.get(arg).content, subs, buf, false);
write_content(subs.get(arg).content, subs, buf, Parens::InFn);
}
buf.push_str(" -> ");
write_content(subs.get(ret).content, subs, buf, false);
write_content(subs.get(ret).content, subs, buf, Parens::InFn);
if use_parens {
buf.push_str(")");

View file

@ -39,13 +39,13 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) {
subs.union(actual, expected);
}
Let(let_con) => {
match let_con.ret_constraint {
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)
}
ref ret_con => {
ret_con => {
// Solve the assignments' constraints first.
solve(env, subs, &let_con.assignments_constraint);

View file

@ -8,7 +8,7 @@ pub struct Subs {
utable: UnificationTable<InPlace<Variable>>,
}
#[derive(Copy, PartialEq, Eq, Clone)]
#[derive(Copy, PartialEq, Eq, Clone, Hash)]
pub struct Variable(u32);
impl Variable {
@ -67,6 +67,10 @@ impl Subs {
self.utable.probe_value(key)
}
pub fn get_root_key(&mut self, key: Variable) -> Variable {
self.utable.get_root_key(key)
}
pub fn set(&mut self, key: Variable, r_value: Descriptor) {
let l_key = self.utable.get_root_key(key);
let unified = unify::unify_var_val(self, l_key, &r_value);
@ -131,8 +135,13 @@ impl From<Content> for Descriptor {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Content {
/// A type variable which the user did not name in an annotation,
///
/// When we auto-generate a type var name, e.g. the "a" in (a -> a), we
/// change the Option in here from None to Some.
FlexVar(Option<Box<str>> /* name - e.g. in pattern matching */),
RigidVar(String /* name given in a user-written annotation */),
/// name given in a user-written annotation
RigidVar(Box<str>),
Structure(FlatType),
Error(Problem),
}
@ -145,7 +154,6 @@ pub enum FlatType {
args: Vec<Variable>,
},
Func(Vec<Variable>, Variable),
BinOp(Variable, Variable, Variable),
Erroneous(Problem),
EmptyRecord,
}

View file

@ -184,9 +184,9 @@ pub enum AnnotationSource {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Reason {
AnonymousFnArg(u8 /* arg index */),
AnonymousFnArg { arg_index: u8 },
NamedFnArg(String /* function name */, u8 /* arg index */),
AnonymousFnCall(u8 /* arity */),
AnonymousFnCall { arity: u8 },
NamedFnCall(String /* function name */, u8 /* arity */),
BinOpArg(BinOp, ArgSide),
BinOpRet(BinOp),

View file

@ -90,14 +90,6 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri
from_content(Error(Problem::MissingArguments))
}
}
(BinOp(l_l_arg, l_r_arg, l_ret), BinOp(r_l_arg, r_r_arg, r_ret)) => {
let l_arg = union_vars(subs, *l_l_arg, *r_l_arg);
let r_arg = union_vars(subs, *l_r_arg, *r_r_arg);
let ret = union_vars(subs, *l_ret, *r_ret);
let flat_type = BinOp(l_arg, r_arg, ret);
from_content(Structure(flat_type))
}
_ => from_content(Error(Problem::GenericMismatch)),
}
}
@ -137,7 +129,7 @@ fn unify_rigid(name: &str, other: &Content) -> Descriptor {
match other {
FlexVar(_) => {
// If the other is flex, rigid wins!
from_content(RigidVar(name.to_string()))
from_content(RigidVar(name.into()))
}
RigidVar(_) | Structure(_) => {
// Type mismatch! Rigid can only unify with flex, even if the

View file

@ -12,7 +12,7 @@ mod helpers;
mod test_infer {
use helpers::can_expr;
use roc::infer::infer_expr;
use roc::pretty_print_types::content_to_string;
use roc::pretty_print_types::{content_to_string, name_all_type_vars};
// HELPERS
@ -20,6 +20,9 @@ mod test_infer {
let (_, output, _, procedures, mut subs, variable) = can_expr(src);
let content = infer_expr(&mut subs, procedures, &output.constraint, variable);
name_all_type_vars(variable, &mut subs);
let actual_str = content_to_string(content, &mut subs);
assert_eq!(actual_str, expected.to_string());
@ -486,7 +489,6 @@ mod test_infer {
}
// TODO type annotations
// TODO fix identity inference
// TODO BoundTypeVariables
// TODO conditionals
@ -530,25 +532,137 @@ mod test_infer {
"Int",
);
}
// #[test]
// fn identity() {
// infer_eq(
// indoc!(r#"
// \val -> val
// "#),
// "a -> a"
// );
// }
// #[test]
// fn always_function() {
// infer_eq(
// indoc!(r#"
// \val -> \_ -> val
// "#),
// "a -> (* -> a)"
// );
// }
#[test]
fn anonymous_identity() {
infer_eq(
indoc!(
r#"
(\a -> a) 3.14
"#
),
"Float",
);
}
#[test]
fn identity_of_identity() {
infer_eq(
indoc!(
r#"
(\val -> val) (\val -> val)
"#
),
"a -> a",
);
}
// #[test]
// TODO FIXME this should work, but instead causes a stack overflow!
// fn recursive_identity() {
// infer_eq(
// indoc!(
// r#"
// identity = \val -> val
// identity identity
// "#
// ),
// "a -> a",
// );
// }
#[test]
fn identity_function() {
infer_eq(
indoc!(
r#"
\val -> val
"#
),
"a -> a",
);
}
#[test]
fn use_apply() {
infer_eq(
indoc!(
r#"
apply = \f x -> f x
identity = \a -> a
apply identity 5
"#
),
"Int",
);
}
#[test]
fn apply_function() {
infer_eq(
indoc!(
r#"
\f x -> f x
"#
),
"(a -> b), a -> b",
);
}
// #[test]
// TODO FIXME this should pass, but instead fails to canonicalize
// fn use_flip() {
// infer_eq(
// indoc!(
// r#"
// flip = \f -> (\a b -> f b a)
// neverendingInt = \f int -> f int
// x = neverendingInt (\a -> a) 5
// flip neverendingInt
// "#
// ),
// "(Int, (a -> a)) -> Int",
// );
// }
#[test]
fn flip_function() {
infer_eq(
indoc!(
r#"
\f -> (\a b -> f b a),
"#
),
"(a, b -> c) -> (b, a -> c)",
);
}
#[test]
fn always_function() {
infer_eq(
indoc!(
r#"
\val -> \_ -> val
"#
),
"a -> (* -> a)",
);
}
#[test]
fn pass_a_function() {
infer_eq(
indoc!(
r#"
\f -> f {}
"#
),
"({} -> a) -> a",
);
}
// OPERATORS