add variable to list

stores the list type, so we can know whether it is unique
This commit is contained in:
Folkert 2020-08-10 21:05:57 +02:00
parent 4de573b54b
commit b4c5c2f793
7 changed files with 118 additions and 30 deletions

View file

@ -58,6 +58,7 @@ pub enum Expr {
Str(Box<str>), Str(Box<str>),
BlockStr(Box<str>), BlockStr(Box<str>),
List { List {
list_var: Variable, // required for uniqueness of the list
elem_var: Variable, elem_var: Variable,
loc_elems: Vec<Located<Expr>>, loc_elems: Vec<Located<Expr>>,
}, },
@ -256,6 +257,7 @@ pub fn canonicalize_expr<'a>(
if loc_elems.is_empty() { if loc_elems.is_empty() {
( (
List { List {
list_var: var_store.fresh(),
elem_var: var_store.fresh(), elem_var: var_store.fresh(),
loc_elems: Vec::new(), loc_elems: Vec::new(),
}, },
@ -283,6 +285,7 @@ pub fn canonicalize_expr<'a>(
( (
List { List {
list_var: var_store.fresh(),
elem_var: var_store.fresh(), elem_var: var_store.fresh(),
loc_elems: can_elems, loc_elems: can_elems,
}, },
@ -1052,6 +1055,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
| other @ RunLowLevel { .. } => other, | other @ RunLowLevel { .. } => other,
List { List {
list_var,
elem_var, elem_var,
loc_elems, loc_elems,
} => { } => {
@ -1067,6 +1071,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
} }
List { List {
list_var,
elem_var, elem_var,
loc_elems: new_elems, loc_elems: new_elems,
} }

View file

@ -203,7 +203,7 @@ pub fn constrain_expr(
List { List {
elem_var, elem_var,
loc_elems, loc_elems,
.. list_var: _unused,
} => { } => {
if loc_elems.is_empty() { if loc_elems.is_empty() {
exists( exists(

View file

@ -639,6 +639,7 @@ pub fn constrain_expr(
exists(vars, And(arg_cons)) exists(vars, And(arg_cons))
} }
List { List {
list_var,
elem_var, elem_var,
loc_elems, loc_elems,
} => { } => {
@ -676,9 +677,12 @@ pub fn constrain_expr(
} }
let inferred = list_type(Bool::variable(uniq_var), entry_type); let inferred = list_type(Bool::variable(uniq_var), entry_type);
constraints.push(Eq(inferred, expected, Category::List, region)); constraints.push(Eq(inferred, expected.clone(), Category::List, region));
exists(vec![*elem_var, uniq_var], And(constraints)) let stored = Type::Variable(*list_var);
constraints.push(Eq(stored, expected, Category::Storage, region));
exists(vec![*elem_var, *list_var, uniq_var], And(constraints))
} }
} }
Var(symbol) => { Var(symbol) => {

View file

@ -844,6 +844,11 @@ pub fn visit_declaration<'a>(arena: &'a Bump, stmt: &'a Stmt<'a>) -> &'a Stmt<'a
pub fn visit_proc<'a>(arena: &'a Bump, proc: &mut Proc<'a>) { pub fn visit_proc<'a>(arena: &'a Bump, proc: &mut Proc<'a>) {
let ctx = Context::new(arena); let ctx = Context::new(arena);
if proc.name.is_builtin() {
// we must take care of our own refcounting in builtins
return;
}
let params = Vec::from_iter_in( let params = Vec::from_iter_in(
proc.args.iter().map(|(layout, symbol)| Param { proc.args.iter().map(|(layout, symbol)| Param {
symbol: *symbol, symbol: *symbol,

View file

@ -1,6 +1,6 @@
use self::InProgressProc::*; use self::InProgressProc::*;
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId}; use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
use crate::layout::{Builtin, Layout, LayoutCache, LayoutProblem, Ownership}; use crate::layout::{Builtin, Layout, LayoutCache, LayoutProblem};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_collections::all::{default_hasher, MutMap, MutSet};
@ -1482,6 +1482,7 @@ pub fn with_hole<'a>(
} }
List { List {
list_var,
elem_var, elem_var,
loc_elems, loc_elems,
} => { } => {
@ -1503,13 +1504,13 @@ pub fn with_hole<'a>(
elem_layout: elem_layout.clone(), elem_layout: elem_layout.clone(),
elems: arg_symbols, elems: arg_symbols,
}; };
let mode = crate::layout::mode_from_var(list_var, env.subs);
let mut stmt = Stmt::Let( let mut stmt = Stmt::Let(
assigned, assigned,
expr, expr,
Layout::Builtin(Builtin::List( Layout::Builtin(Builtin::List(mode, env.arena.alloc(elem_layout))),
Ownership::Owned,
env.arena.alloc(elem_layout),
)),
hole, hole,
); );

View file

@ -28,17 +28,9 @@ pub enum Layout<'a> {
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
pub enum Ownership { pub enum MemoryMode {
/// The default. Object is reference counted
Owned,
/// Do not update reference counter, surrounding context
/// keeps this value alive
Borrowed,
/// Object is unique, can be mutated in-place and
/// is not reference counted
Unique, Unique,
Refcounted,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -56,7 +48,7 @@ pub enum Builtin<'a> {
Str, Str,
Map(&'a Layout<'a>, &'a Layout<'a>), Map(&'a Layout<'a>, &'a Layout<'a>),
Set(&'a Layout<'a>), Set(&'a Layout<'a>),
List(Ownership, &'a Layout<'a>), List(MemoryMode, &'a Layout<'a>),
EmptyStr, EmptyStr,
EmptyList, EmptyList,
EmptyMap, EmptyMap,
@ -270,7 +262,12 @@ impl<'a> Builtin<'a> {
match self { match self {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32 Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32
| Float16 | EmptyStr | EmptyMap | EmptyList | EmptySet => false, | Float16 | EmptyStr | EmptyMap | EmptyList | EmptySet => false,
Str | Map(_, _) | Set(_) | List(_, _) => true, List(mode, element_layout) => match mode {
MemoryMode::Refcounted => true,
MemoryMode::Unique => element_layout.contains_refcounted(),
},
Str | Map(_, _) | Set(_) => true,
} }
} }
} }
@ -308,13 +305,26 @@ fn layout_from_flat_type<'a>(
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
// The first argument is the uniqueness info; // The first argument is the uniqueness info;
// that doesn't affect layout, so we don't need it here. // second is the base type
let wrapped_var = args[1]; let wrapped_var = args[1];
// For now, layout is unaffected by uniqueness. // correct the memory mode of unique lists
// (Incorporating refcounting may change this.) match Layout::from_var(arena, wrapped_var, subs)? {
// Unwrap and continue Layout::Builtin(Builtin::List(_, elem_layout)) => {
Layout::from_var(arena, wrapped_var, subs) let uniqueness_var = args[0];
let uniqueness_content =
subs.get_without_compacting(uniqueness_var).content;
let mode = if uniqueness_content.is_unique(subs) {
MemoryMode::Unique
} else {
MemoryMode::Refcounted
};
Ok(Layout::Builtin(Builtin::List(mode, elem_layout)))
}
other => Ok(other),
}
} }
_ => { _ => {
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args)); panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
@ -706,15 +716,15 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
pub fn list_layout_from_elem<'a>( pub fn list_layout_from_elem<'a>(
arena: &'a Bump, arena: &'a Bump,
subs: &Subs, subs: &Subs,
var: Variable, elem_var: Variable,
) -> Result<Layout<'a>, LayoutProblem> { ) -> Result<Layout<'a>, LayoutProblem> {
match subs.get_without_compacting(var).content { match subs.get_without_compacting(elem_var).content {
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => { Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let arg_var = args.get(1).unwrap(); let var = *args.get(1).unwrap();
list_layout_from_elem(arena, subs, *arg_var) list_layout_from_elem(arena, subs, var)
} }
Content::FlexVar(_) | Content::RigidVar(_) => { Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (List *) then it must have been an empty list // If this was still a (List *) then it must have been an empty list
@ -725,9 +735,27 @@ pub fn list_layout_from_elem<'a>(
// This is a normal list. // This is a normal list.
Ok(Layout::Builtin(Builtin::List( Ok(Layout::Builtin(Builtin::List(
Ownership::Owned, MemoryMode::Refcounted,
arena.alloc(elem_layout), arena.alloc(elem_layout),
))) )))
} }
} }
} }
pub fn mode_from_var(var: Variable, subs: &Subs) -> MemoryMode {
match subs.get_without_compacting(var).content {
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
debug_assert_eq!(args.len(), 2);
let uvar = *args.get(0).unwrap();
let content = subs.get_without_compacting(uvar).content;
if content.is_unique(subs) {
MemoryMode::Unique
} else {
MemoryMode::Refcounted
}
}
_ => MemoryMode::Refcounted,
}
}

View file

@ -989,4 +989,49 @@ mod test_mono {
), ),
) )
} }
#[test]
fn list_pass_to_function() {
compiles_to_ir(
indoc!(
r#"
x : List Int
x = [1,2,3]
id : List Int -> List Int
id = \y -> List.set y 0 0
id x
"#
),
indoc!(
r#"
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
let Test.12 = lowlevel ListLen #Attr.2;
let Test.11 = lowlevel NumLt #Attr.3 Test.12;
if Test.11 then
let Test.9 = lowlevel ListSet #Attr.2 #Attr.3 #Attr.4;
jump Test.10 Test.9;
else
jump Test.10 #Attr.2;
joinpoint Test.10 Test.8:
ret Test.8;
procedure Test.1 (Test.3):
let Test.6 = 0i64;
let Test.7 = 0i64;
let Test.5 = CallByName List.4 Test.3 Test.6 Test.7;
ret Test.5;
let Test.13 = 1i64;
let Test.14 = 2i64;
let Test.15 = 3i64;
let Test.0 = Array [Test.13, Test.14, Test.15];
let Test.4 = CallByName Test.1 Test.0;
dec Test.0;
ret Test.4;
"#
),
)
}
} }