mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
add variable to list
stores the list type, so we can know whether it is unique
This commit is contained in:
parent
4de573b54b
commit
b4c5c2f793
7 changed files with 118 additions and 30 deletions
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue