mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
1893 lines
69 KiB
Rust
1893 lines
69 KiB
Rust
use crate::ir::{substitute_in_exprs, Env, Expr, Procs, Stmt};
|
|
use crate::layout::{
|
|
self, Builtin, InLayout, Layout, LayoutCache, LayoutInterner, LayoutProblem, LayoutRepr,
|
|
TagIdIntType, UnionLayout, WrappedVariant,
|
|
};
|
|
use bumpalo::collections::Vec;
|
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
|
use roc_collections::all::{BumpMap, BumpMapDefault};
|
|
use roc_error_macros::internal_error;
|
|
use roc_exhaustive::{Ctor, CtorName, ListArity, RenderAs, TagId};
|
|
use roc_module::ident::{Lowercase, TagName};
|
|
use roc_module::low_level::LowLevel;
|
|
use roc_module::symbol::Symbol;
|
|
use roc_problem::can::{RuntimeError, ShadowKind};
|
|
use roc_types::subs::Variable;
|
|
|
|
use super::literal::{make_num_literal, IntOrFloatValue};
|
|
use super::{Call, CallType, Literal};
|
|
|
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
|
/// codegen can generate a runtime error if this pattern is reached.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Pattern<'a> {
|
|
Identifier(Symbol),
|
|
Underscore,
|
|
As(Box<Pattern<'a>>, Symbol),
|
|
IntLiteral([u8; 16], IntWidth),
|
|
FloatLiteral(u64, FloatWidth),
|
|
DecimalLiteral([u8; 16]),
|
|
BitLiteral {
|
|
value: bool,
|
|
tag_name: TagName,
|
|
union: roc_exhaustive::Union,
|
|
},
|
|
EnumLiteral {
|
|
tag_id: u8,
|
|
tag_name: TagName,
|
|
union: roc_exhaustive::Union,
|
|
},
|
|
StrLiteral(Box<str>),
|
|
|
|
RecordDestructure(Vec<'a, RecordDestruct<'a>>, &'a [InLayout<'a>]),
|
|
TupleDestructure(Vec<'a, TupleDestruct<'a>>, &'a [InLayout<'a>]),
|
|
NewtypeDestructure {
|
|
tag_name: TagName,
|
|
arguments: Vec<'a, (Pattern<'a>, InLayout<'a>)>,
|
|
},
|
|
AppliedTag {
|
|
tag_name: TagName,
|
|
tag_id: TagIdIntType,
|
|
arguments: Vec<'a, (Pattern<'a>, InLayout<'a>)>,
|
|
layout: UnionLayout<'a>,
|
|
union: roc_exhaustive::Union,
|
|
},
|
|
Voided {
|
|
tag_name: TagName,
|
|
},
|
|
OpaqueUnwrap {
|
|
opaque: Symbol,
|
|
argument: Box<(Pattern<'a>, InLayout<'a>)>,
|
|
},
|
|
List {
|
|
arity: ListArity,
|
|
list_layout: InLayout<'a>,
|
|
element_layout: InLayout<'a>,
|
|
elements: Vec<'a, Pattern<'a>>,
|
|
opt_rest: Option<(usize, Option<Symbol>)>,
|
|
},
|
|
}
|
|
|
|
impl<'a> Pattern<'a> {
|
|
/// This pattern contains a pattern match on Void (i.e. [], the empty tag union)
|
|
/// such branches are not reachable at runtime
|
|
pub fn is_voided(&self) -> bool {
|
|
let mut stack: std::vec::Vec<&Pattern> = vec![self];
|
|
|
|
while let Some(pattern) = stack.pop() {
|
|
match pattern {
|
|
Pattern::Identifier(_)
|
|
| Pattern::Underscore
|
|
| Pattern::IntLiteral(_, _)
|
|
| Pattern::FloatLiteral(_, _)
|
|
| Pattern::DecimalLiteral(_)
|
|
| Pattern::BitLiteral { .. }
|
|
| Pattern::EnumLiteral { .. }
|
|
| Pattern::StrLiteral(_) => { /* terminal */ }
|
|
Pattern::As(subpattern, _) => stack.push(subpattern),
|
|
Pattern::RecordDestructure(destructs, _) => {
|
|
for destruct in destructs {
|
|
match &destruct.typ {
|
|
DestructType::Required(_) => { /* do nothing */ }
|
|
DestructType::Guard(pattern) => {
|
|
stack.push(pattern);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Pattern::TupleDestructure(destructs, _) => {
|
|
for destruct in destructs {
|
|
stack.push(&destruct.pat);
|
|
}
|
|
}
|
|
Pattern::NewtypeDestructure { arguments, .. } => {
|
|
stack.extend(arguments.iter().map(|(t, _)| t))
|
|
}
|
|
Pattern::Voided { .. } => return true,
|
|
Pattern::AppliedTag { arguments, .. } => {
|
|
stack.extend(arguments.iter().map(|(t, _)| t))
|
|
}
|
|
Pattern::OpaqueUnwrap { argument, .. } => stack.push(&argument.0),
|
|
Pattern::List { elements, .. } => stack.extend(elements),
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn collect_symbols(
|
|
&self,
|
|
layout: InLayout<'a>,
|
|
) -> impl Iterator<Item = (Symbol, InLayout<'a>)> + '_ {
|
|
PatternBindingIter::One(self, layout)
|
|
}
|
|
}
|
|
|
|
enum PatternBindingIter<'r, 'a> {
|
|
Done,
|
|
One(&'r Pattern<'a>, InLayout<'a>),
|
|
Stack(std::vec::Vec<(PatternBindingWork<'r, 'a>, InLayout<'a>)>),
|
|
}
|
|
|
|
enum PatternBindingWork<'r, 'a> {
|
|
Pat(&'r Pattern<'a>),
|
|
RecordDestruct(&'r DestructType<'a>),
|
|
}
|
|
|
|
impl<'r, 'a> Iterator for PatternBindingIter<'r, 'a> {
|
|
type Item = (Symbol, InLayout<'a>);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
use Pattern::*;
|
|
use PatternBindingIter::*;
|
|
use PatternBindingWork::*;
|
|
match self {
|
|
Done => None,
|
|
One(pattern, layout) => {
|
|
let layout = *layout;
|
|
match pattern {
|
|
Identifier(symbol) => {
|
|
*self = Done;
|
|
(*symbol, layout).into()
|
|
}
|
|
Underscore => None,
|
|
As(pat, symbol) => {
|
|
*self = One(pat, layout);
|
|
(*symbol, layout).into()
|
|
}
|
|
RecordDestructure(destructs, _) => {
|
|
let stack = destructs
|
|
.iter()
|
|
.map(|destruct| (RecordDestruct(&destruct.typ), destruct.layout))
|
|
.rev()
|
|
.collect();
|
|
*self = Stack(stack);
|
|
self.next()
|
|
}
|
|
TupleDestructure(destructs, _) => {
|
|
let stack = destructs
|
|
.iter()
|
|
.map(|destruct| (Pat(&destruct.pat), destruct.layout))
|
|
.rev()
|
|
.collect();
|
|
*self = Stack(stack);
|
|
self.next()
|
|
}
|
|
NewtypeDestructure { arguments, .. } | AppliedTag { arguments, .. } => {
|
|
let stack = arguments.iter().map(|(p, l)| (Pat(p), *l)).rev().collect();
|
|
*self = Stack(stack);
|
|
self.next()
|
|
}
|
|
OpaqueUnwrap { argument, .. } => {
|
|
*self = One(&argument.0, layout);
|
|
self.next()
|
|
}
|
|
List {
|
|
element_layout,
|
|
elements,
|
|
..
|
|
} => {
|
|
let stack = elements
|
|
.iter()
|
|
.map(|p| (Pat(p), *element_layout))
|
|
.rev()
|
|
.collect();
|
|
*self = Stack(stack);
|
|
self.next()
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| BitLiteral { .. }
|
|
| EnumLiteral { .. }
|
|
| StrLiteral(_)
|
|
| Voided { .. } => None,
|
|
}
|
|
}
|
|
Stack(stack) => {
|
|
while let Some((pat, layout)) = stack.pop() {
|
|
match pat {
|
|
Pat(pattern) => match pattern {
|
|
Identifier(symbol) => return (*symbol, layout).into(),
|
|
As(pat, symbol) => {
|
|
stack.push((Pat(pat), layout));
|
|
return (*symbol, layout).into();
|
|
}
|
|
RecordDestructure(destructs, _) => stack.extend(
|
|
destructs
|
|
.iter()
|
|
.map(|destruct| {
|
|
(RecordDestruct(&destruct.typ), destruct.layout)
|
|
})
|
|
.rev(),
|
|
),
|
|
TupleDestructure(destructs, _) => stack.extend(
|
|
destructs
|
|
.iter()
|
|
.map(|destruct| (Pat(&destruct.pat), destruct.layout))
|
|
.rev(),
|
|
),
|
|
NewtypeDestructure { arguments, .. } | AppliedTag { arguments, .. } => {
|
|
stack.extend(arguments.iter().map(|(p, l)| (Pat(p), *l)).rev())
|
|
}
|
|
OpaqueUnwrap { argument, .. } => {
|
|
stack.push((Pat(&argument.0), layout));
|
|
}
|
|
List {
|
|
element_layout,
|
|
elements,
|
|
..
|
|
} => {
|
|
stack.extend(
|
|
elements.iter().map(|p| (Pat(p), *element_layout)).rev(),
|
|
);
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| BitLiteral { .. }
|
|
| EnumLiteral { .. }
|
|
| Underscore
|
|
| StrLiteral(_)
|
|
| Voided { .. } => {}
|
|
},
|
|
PatternBindingWork::RecordDestruct(_) => todo!(),
|
|
}
|
|
}
|
|
|
|
*self = Done;
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct RecordDestruct<'a> {
|
|
pub label: Lowercase,
|
|
pub variable: Variable,
|
|
pub layout: InLayout<'a>,
|
|
pub typ: DestructType<'a>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct TupleDestruct<'a> {
|
|
pub index: usize,
|
|
pub variable: Variable,
|
|
pub layout: InLayout<'a>,
|
|
pub pat: Pattern<'a>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum DestructType<'a> {
|
|
Required(Symbol),
|
|
Guard(Pattern<'a>),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct WhenBranch<'a> {
|
|
pub patterns: Vec<'a, Pattern<'a>>,
|
|
pub value: Expr<'a>,
|
|
pub guard: Option<Stmt<'a>>,
|
|
}
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
pub fn from_can_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_pattern: &roc_can::pattern::Pattern,
|
|
) -> Result<
|
|
(
|
|
Pattern<'a>,
|
|
Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
|
|
),
|
|
RuntimeError,
|
|
> {
|
|
let mut assignments = Vec::new_in(env.arena);
|
|
let pattern = from_can_pattern_help(env, procs, layout_cache, can_pattern, &mut assignments)?;
|
|
|
|
Ok((pattern, assignments))
|
|
}
|
|
|
|
fn from_can_pattern_help<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_pattern: &roc_can::pattern::Pattern,
|
|
assignments: &mut Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
|
|
) -> Result<Pattern<'a>, RuntimeError> {
|
|
use roc_can::pattern::Pattern::*;
|
|
|
|
match can_pattern {
|
|
Underscore => Ok(Pattern::Underscore),
|
|
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
|
As(subpattern, symbol) => {
|
|
let mono_subpattern =
|
|
from_can_pattern_help(env, procs, layout_cache, &subpattern.value, assignments)?;
|
|
|
|
Ok(Pattern::As(Box::new(mono_subpattern), *symbol))
|
|
}
|
|
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
|
|
IntLiteral(var, _, int_str, int, _bound) => Ok(make_num_literal_pattern(
|
|
env,
|
|
layout_cache,
|
|
*var,
|
|
int_str,
|
|
IntOrFloatValue::Int(*int),
|
|
)),
|
|
FloatLiteral(var, _, float_str, float, _bound) => Ok(make_num_literal_pattern(
|
|
env,
|
|
layout_cache,
|
|
*var,
|
|
float_str,
|
|
IntOrFloatValue::Float(*float),
|
|
)),
|
|
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
|
|
SingleQuote(var, _, c, _) => {
|
|
let layout = layout_cache.from_var(env.arena, *var, env.subs);
|
|
match layout.map(|l| layout_cache.get_repr(l)) {
|
|
Ok(LayoutRepr::Builtin(Builtin::Int(width))) => {
|
|
Ok(Pattern::IntLiteral((*c as i128).to_ne_bytes(), width))
|
|
}
|
|
o => internal_error!("an integer width was expected, but we found {:?}", o),
|
|
}
|
|
}
|
|
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
|
|
original_region: *region,
|
|
shadow: ident.clone(),
|
|
kind: ShadowKind::Variable,
|
|
}),
|
|
UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)),
|
|
MalformedPattern(_problem, region) => {
|
|
// TODO preserve malformed problem information here?
|
|
Err(RuntimeError::UnsupportedPattern(*region))
|
|
}
|
|
OpaqueNotInScope(loc_ident) => {
|
|
// TODO(opaques) should be `RuntimeError::OpaqueNotDefined`
|
|
Err(RuntimeError::UnsupportedPattern(loc_ident.region))
|
|
}
|
|
NumLiteral(var, num_str, num, _bound) => Ok(make_num_literal_pattern(
|
|
env,
|
|
layout_cache,
|
|
*var,
|
|
num_str,
|
|
IntOrFloatValue::Int(*num),
|
|
)),
|
|
|
|
AppliedTag {
|
|
whole_var,
|
|
tag_name,
|
|
arguments,
|
|
..
|
|
} => {
|
|
use crate::layout::UnionVariant::*;
|
|
use roc_exhaustive::Union;
|
|
|
|
let res_variant = {
|
|
let mut layout_env =
|
|
layout::Env::from_components(layout_cache, env.subs, env.arena);
|
|
crate::layout::union_sorted_tags(&mut layout_env, *whole_var).map_err(Into::into)
|
|
};
|
|
|
|
let variant = match res_variant {
|
|
Ok(cached) => cached,
|
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
return Err(RuntimeError::UnresolvedTypeVar)
|
|
}
|
|
Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType),
|
|
};
|
|
|
|
let result = match variant {
|
|
Never => unreachable!(
|
|
"there is no pattern of type `[]`, union var {:?}",
|
|
*whole_var
|
|
),
|
|
Unit => Pattern::EnumLiteral {
|
|
tag_id: 0,
|
|
tag_name: tag_name.clone(),
|
|
union: Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: vec![Ctor {
|
|
tag_id: TagId(0),
|
|
name: CtorName::Tag(tag_name.clone()),
|
|
arity: 0,
|
|
}],
|
|
},
|
|
},
|
|
BoolUnion { ttrue, ffalse } => {
|
|
let (ttrue, ffalse) = (ttrue.expect_tag(), ffalse.expect_tag());
|
|
Pattern::BitLiteral {
|
|
value: tag_name == &ttrue,
|
|
tag_name: tag_name.clone(),
|
|
union: Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: vec![
|
|
Ctor {
|
|
tag_id: TagId(0),
|
|
name: CtorName::Tag(ffalse),
|
|
arity: 0,
|
|
},
|
|
Ctor {
|
|
tag_id: TagId(1),
|
|
name: CtorName::Tag(ttrue),
|
|
arity: 0,
|
|
},
|
|
],
|
|
},
|
|
}
|
|
}
|
|
ByteUnion(tag_names) => {
|
|
let tag_id = tag_names
|
|
.iter()
|
|
.position(|key| tag_name == key.expect_tag_ref())
|
|
.expect("tag must be in its own type");
|
|
|
|
let mut ctors = std::vec::Vec::with_capacity(tag_names.len());
|
|
for (i, tag_name) in tag_names.into_iter().enumerate() {
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(i as _),
|
|
name: CtorName::Tag(tag_name.expect_tag()),
|
|
arity: 0,
|
|
})
|
|
}
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
Pattern::EnumLiteral {
|
|
tag_id: tag_id as u8,
|
|
tag_name: tag_name.clone(),
|
|
union,
|
|
}
|
|
}
|
|
Newtype {
|
|
arguments: field_layouts,
|
|
..
|
|
} => {
|
|
let mut arguments = arguments.clone();
|
|
|
|
arguments.sort_by(|arg1, arg2| {
|
|
let size1 = layout_cache
|
|
.from_var(env.arena, arg1.0, env.subs)
|
|
.map(|x| layout_cache.interner.alignment_bytes(x))
|
|
.unwrap_or(0);
|
|
|
|
let size2 = layout_cache
|
|
.from_var(env.arena, arg2.0, env.subs)
|
|
.map(|x| layout_cache.interner.alignment_bytes(x))
|
|
.unwrap_or(0);
|
|
|
|
size2.cmp(&size1)
|
|
});
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(field_layouts.iter()) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::NewtypeDestructure {
|
|
tag_name: tag_name.clone(),
|
|
arguments: mono_args,
|
|
}
|
|
}
|
|
NewtypeByVoid {
|
|
data_tag_arguments,
|
|
data_tag_name,
|
|
..
|
|
} => {
|
|
let data_tag_name = data_tag_name.expect_tag();
|
|
|
|
if tag_name != &data_tag_name {
|
|
// this tag is not represented at runtime
|
|
Pattern::Voided {
|
|
tag_name: tag_name.clone(),
|
|
}
|
|
} else {
|
|
let mut arguments = arguments.clone();
|
|
|
|
arguments.sort_by(|arg1, arg2| {
|
|
let size1 = layout_cache
|
|
.from_var(env.arena, arg1.0, env.subs)
|
|
.map(|x| layout_cache.interner.alignment_bytes(x))
|
|
.unwrap_or(0);
|
|
|
|
let size2 = layout_cache
|
|
.from_var(env.arena, arg2.0, env.subs)
|
|
.map(|x| layout_cache.interner.alignment_bytes(x))
|
|
.unwrap_or(0);
|
|
|
|
size2.cmp(&size1)
|
|
});
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
let it = arguments.iter().zip(data_tag_arguments.iter());
|
|
for ((_, loc_pat), layout) in it {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::NewtypeDestructure {
|
|
tag_name: tag_name.clone(),
|
|
arguments: mono_args,
|
|
}
|
|
}
|
|
}
|
|
|
|
Wrapped(variant) => {
|
|
let (tag_id, argument_layouts) = variant.tag_name_to_id(tag_name);
|
|
let number_of_tags = variant.number_of_tags();
|
|
let mut ctors = std::vec::Vec::with_capacity(number_of_tags);
|
|
|
|
let arguments = {
|
|
let mut temp = arguments.clone();
|
|
|
|
temp.sort_by(|arg1, arg2| {
|
|
let layout1 =
|
|
layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap();
|
|
let layout2 =
|
|
layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap();
|
|
|
|
let size1 = layout_cache.interner.alignment_bytes(layout1);
|
|
let size2 = layout_cache.interner.alignment_bytes(layout2);
|
|
|
|
size2.cmp(&size1)
|
|
});
|
|
|
|
temp
|
|
};
|
|
|
|
// we must derive the union layout from the whole_var, building it up
|
|
// from `layouts` would unroll recursive tag unions, and that leads to
|
|
// problems down the line because we hash layouts and an unrolled
|
|
// version is not the same as the minimal version.
|
|
let whole_var_layout = layout_cache.from_var(env.arena, *whole_var, env.subs);
|
|
let layout =
|
|
match whole_var_layout.map(|l| layout_cache.interner.chase_recursive(l)) {
|
|
Ok(LayoutRepr::Union(ul)) => ul,
|
|
_ => internal_error!(),
|
|
};
|
|
|
|
use WrappedVariant::*;
|
|
match variant {
|
|
NonRecursive {
|
|
sorted_tag_layouts: ref tags,
|
|
} => {
|
|
debug_assert!(tags.len() > 1);
|
|
|
|
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(i as _),
|
|
name: CtorName::Tag(tag_name.expect_tag_ref().clone()),
|
|
arity: args.len(),
|
|
})
|
|
}
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
|
|
debug_assert_eq!(
|
|
arguments.len(),
|
|
argument_layouts.len(),
|
|
"The {:?} tag got {} arguments, but its layout expects {}!",
|
|
tag_name,
|
|
arguments.len(),
|
|
argument_layouts.len(),
|
|
);
|
|
let it = argument_layouts.iter();
|
|
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::AppliedTag {
|
|
tag_name: tag_name.clone(),
|
|
tag_id: tag_id as _,
|
|
arguments: mono_args,
|
|
union,
|
|
layout,
|
|
}
|
|
}
|
|
|
|
Recursive {
|
|
sorted_tag_layouts: ref tags,
|
|
} => {
|
|
debug_assert!(tags.len() > 1);
|
|
|
|
for (i, (tag_name, args)) in tags.iter().enumerate() {
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(i as _),
|
|
name: CtorName::Tag(tag_name.expect_tag_ref().clone()),
|
|
arity: args.len(),
|
|
})
|
|
}
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
|
|
debug_assert_eq!(arguments.len(), argument_layouts.len());
|
|
let it = argument_layouts.iter();
|
|
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::AppliedTag {
|
|
tag_name: tag_name.clone(),
|
|
tag_id: tag_id as _,
|
|
arguments: mono_args,
|
|
union,
|
|
layout,
|
|
}
|
|
}
|
|
|
|
NonNullableUnwrapped {
|
|
tag_name: w_tag_name,
|
|
fields,
|
|
} => {
|
|
debug_assert_eq!(w_tag_name.expect_tag_ref(), tag_name);
|
|
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(0),
|
|
name: CtorName::Tag(tag_name.clone()),
|
|
arity: fields.len(),
|
|
});
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
|
|
debug_assert_eq!(arguments.len(), argument_layouts.len());
|
|
let it = argument_layouts.iter();
|
|
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::AppliedTag {
|
|
tag_name: tag_name.clone(),
|
|
tag_id: tag_id as _,
|
|
arguments: mono_args,
|
|
union,
|
|
layout,
|
|
}
|
|
}
|
|
|
|
NullableWrapped {
|
|
sorted_tag_layouts: ref non_nulled_tags,
|
|
nullable_id,
|
|
nullable_name,
|
|
} => {
|
|
for id in 0..(non_nulled_tags.len() + 1) {
|
|
if id == nullable_id as usize {
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(id as _),
|
|
name: CtorName::Tag(nullable_name.expect_tag_ref().clone()),
|
|
arity: 0,
|
|
});
|
|
} else {
|
|
let i = if id < nullable_id.into() { id } else { id - 1 };
|
|
let (tag_name, args) = &non_nulled_tags[i];
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(i as _),
|
|
name: CtorName::Tag(tag_name.expect_tag_ref().clone()),
|
|
arity: args.len(),
|
|
});
|
|
}
|
|
}
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
|
|
let it = if tag_name == nullable_name.expect_tag_ref() {
|
|
[].iter()
|
|
} else {
|
|
argument_layouts.iter()
|
|
};
|
|
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::AppliedTag {
|
|
tag_name: tag_name.clone(),
|
|
tag_id: tag_id as _,
|
|
arguments: mono_args,
|
|
union,
|
|
layout,
|
|
}
|
|
}
|
|
|
|
NullableUnwrapped {
|
|
other_fields,
|
|
nullable_id,
|
|
nullable_name,
|
|
other_name: _,
|
|
} => {
|
|
debug_assert!(!other_fields.is_empty());
|
|
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(nullable_id as _),
|
|
name: CtorName::Tag(nullable_name.expect_tag_ref().clone()),
|
|
arity: 0,
|
|
});
|
|
|
|
ctors.push(Ctor {
|
|
tag_id: TagId(!nullable_id as _),
|
|
name: CtorName::Tag(nullable_name.expect_tag_ref().clone()),
|
|
arity: other_fields.len(),
|
|
});
|
|
|
|
let union = roc_exhaustive::Union {
|
|
render_as: RenderAs::Tag,
|
|
alternatives: ctors,
|
|
};
|
|
|
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
|
|
let it = if tag_name == nullable_name.expect_tag_ref() {
|
|
[].iter()
|
|
} else {
|
|
argument_layouts.iter()
|
|
};
|
|
|
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
|
mono_args.push((
|
|
from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_pat.value,
|
|
assignments,
|
|
)?,
|
|
*layout,
|
|
));
|
|
}
|
|
|
|
Pattern::AppliedTag {
|
|
tag_name: tag_name.clone(),
|
|
tag_id: tag_id as _,
|
|
arguments: mono_args,
|
|
union,
|
|
layout,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
UnwrappedOpaque {
|
|
opaque, argument, ..
|
|
} => {
|
|
let (arg_var, loc_arg_pattern) = &(**argument);
|
|
let arg_layout = layout_cache
|
|
.from_var(env.arena, *arg_var, env.subs)
|
|
.unwrap();
|
|
let mono_arg_pattern = from_can_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&loc_arg_pattern.value,
|
|
assignments,
|
|
)?;
|
|
Ok(Pattern::OpaqueUnwrap {
|
|
opaque: *opaque,
|
|
argument: Box::new((mono_arg_pattern, arg_layout)),
|
|
})
|
|
}
|
|
|
|
TupleDestructure {
|
|
whole_var,
|
|
destructs,
|
|
..
|
|
} => {
|
|
// sorted fields based on the type
|
|
let sorted_elems = {
|
|
let mut layout_env =
|
|
layout::Env::from_components(layout_cache, env.subs, env.arena);
|
|
crate::layout::sort_tuple_elems(&mut layout_env, *whole_var)
|
|
.map_err(RuntimeError::from)?
|
|
};
|
|
|
|
// sorted fields based on the destruct
|
|
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
|
let mut destructs_by_index = Vec::with_capacity_in(destructs.len(), env.arena);
|
|
destructs_by_index.extend(destructs.iter().map(Some));
|
|
|
|
let mut elem_layouts = Vec::with_capacity_in(sorted_elems.len(), env.arena);
|
|
|
|
for (index, variable, res_layout) in sorted_elems.into_iter() {
|
|
if index < destructs.len() {
|
|
// this elem is destructured by the pattern
|
|
mono_destructs.push(from_can_tuple_destruct(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&destructs[index].value,
|
|
res_layout,
|
|
assignments,
|
|
)?);
|
|
} else {
|
|
// this elem is not destructured by the pattern
|
|
// put in an underscore
|
|
mono_destructs.push(TupleDestruct {
|
|
index,
|
|
variable,
|
|
layout: res_layout,
|
|
pat: Pattern::Underscore,
|
|
});
|
|
}
|
|
|
|
// the layout of this field is part of the layout of the record
|
|
elem_layouts.push(res_layout);
|
|
}
|
|
|
|
Ok(Pattern::TupleDestructure(
|
|
mono_destructs,
|
|
elem_layouts.into_bump_slice(),
|
|
))
|
|
}
|
|
|
|
RecordDestructure {
|
|
whole_var,
|
|
destructs,
|
|
..
|
|
} => {
|
|
// sorted fields based on the type
|
|
let sorted_fields = {
|
|
let mut layout_env =
|
|
layout::Env::from_components(layout_cache, env.subs, env.arena);
|
|
crate::layout::sort_record_fields(&mut layout_env, *whole_var)
|
|
.map_err(RuntimeError::from)?
|
|
};
|
|
|
|
// sorted fields based on the destruct
|
|
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
|
let mut destructs_by_label = BumpMap::with_capacity_in(destructs.len(), env.arena);
|
|
destructs_by_label.extend(destructs.iter().map(|x| (&x.value.label, x)));
|
|
|
|
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
|
|
|
// next we step through both sequences of fields. The outer loop is the sequence based
|
|
// on the type, since not all fields need to actually be destructured in the source
|
|
// language.
|
|
//
|
|
// However in mono patterns, we do destruct all patterns (but use Underscore) when
|
|
// in the source the field is not matche in the source language.
|
|
//
|
|
// Optional fields somewhat complicate the matter here
|
|
|
|
for (label, variable, res_layout) in sorted_fields.into_iter() {
|
|
match res_layout {
|
|
Ok(field_layout) => {
|
|
// the field is non-optional according to the type
|
|
|
|
match destructs_by_label.remove(&label) {
|
|
Some(destruct) => {
|
|
// this field is destructured by the pattern
|
|
mono_destructs.push(from_can_record_destruct(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&destruct.value,
|
|
field_layout,
|
|
assignments,
|
|
)?);
|
|
}
|
|
None => {
|
|
// this field is not destructured by the pattern
|
|
// put in an underscore
|
|
mono_destructs.push(RecordDestruct {
|
|
label: label.clone(),
|
|
variable,
|
|
layout: field_layout,
|
|
typ: DestructType::Guard(Pattern::Underscore),
|
|
});
|
|
}
|
|
}
|
|
|
|
// the layout of this field is part of the layout of the record
|
|
field_layouts.push(field_layout);
|
|
}
|
|
Err(field_layout) => {
|
|
// the field is optional according to the type
|
|
match destructs_by_label.remove(&label) {
|
|
Some(destruct) => {
|
|
// this field is destructured by the pattern
|
|
match &destruct.value.typ {
|
|
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
|
|
// if we reach this stage, the optional field is not present
|
|
// so we push the default assignment into the branch
|
|
assignments.push((
|
|
destruct.value.symbol,
|
|
variable,
|
|
loc_expr.value.clone(),
|
|
));
|
|
}
|
|
_ => unreachable!(
|
|
"only optional destructs can be optional fields"
|
|
),
|
|
};
|
|
}
|
|
None => {
|
|
// this field is not destructured by the pattern
|
|
// put in an underscore
|
|
mono_destructs.push(RecordDestruct {
|
|
label: label.clone(),
|
|
variable,
|
|
layout: field_layout,
|
|
typ: DestructType::Guard(Pattern::Underscore),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (_, destruct) in destructs_by_label.drain() {
|
|
// this destruct is not in the type, but is in the pattern
|
|
// it must be an optional field, and we will use the default
|
|
match &destruct.value.typ {
|
|
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
|
|
assignments.push((
|
|
destruct.value.symbol,
|
|
// destruct.value.var,
|
|
*field_var,
|
|
loc_expr.value.clone(),
|
|
));
|
|
}
|
|
_ => unreachable!("only optional destructs can be optional fields"),
|
|
}
|
|
}
|
|
|
|
Ok(Pattern::RecordDestructure(
|
|
mono_destructs,
|
|
field_layouts.into_bump_slice(),
|
|
))
|
|
}
|
|
|
|
List {
|
|
list_var,
|
|
elem_var,
|
|
patterns,
|
|
} => {
|
|
let list_layout = match layout_cache.from_var(env.arena, *list_var, env.subs) {
|
|
Ok(lay) => lay,
|
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
return Err(RuntimeError::UnresolvedTypeVar)
|
|
}
|
|
Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType),
|
|
};
|
|
|
|
let element_layout = match layout_cache.from_var(env.arena, *elem_var, env.subs) {
|
|
Ok(lay) => lay,
|
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
return Err(RuntimeError::UnresolvedTypeVar)
|
|
}
|
|
Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType),
|
|
};
|
|
|
|
let arity = patterns.arity();
|
|
|
|
let mut mono_patterns = Vec::with_capacity_in(patterns.patterns.len(), env.arena);
|
|
for loc_pat in patterns.patterns.iter() {
|
|
let mono_pat =
|
|
from_can_pattern_help(env, procs, layout_cache, &loc_pat.value, assignments)?;
|
|
mono_patterns.push(mono_pat);
|
|
}
|
|
|
|
Ok(Pattern::List {
|
|
arity,
|
|
list_layout,
|
|
element_layout,
|
|
elements: mono_patterns,
|
|
opt_rest: patterns.opt_rest,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
fn make_num_literal_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
variable: Variable,
|
|
num_str: &str,
|
|
num_value: IntOrFloatValue,
|
|
) -> Pattern<'a> {
|
|
let layout = layout_cache
|
|
.from_var(env.arena, variable, env.subs)
|
|
.unwrap();
|
|
let literal = make_num_literal(&layout_cache.interner, layout, num_str, num_value);
|
|
literal.to_pattern()
|
|
}
|
|
|
|
fn from_can_record_destruct<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_rd: &roc_can::pattern::RecordDestruct,
|
|
field_layout: InLayout<'a>,
|
|
assignments: &mut Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
|
|
) -> Result<RecordDestruct<'a>, RuntimeError> {
|
|
Ok(RecordDestruct {
|
|
label: can_rd.label.clone(),
|
|
variable: can_rd.var,
|
|
layout: field_layout,
|
|
typ: match &can_rd.typ {
|
|
roc_can::pattern::DestructType::Required => DestructType::Required(can_rd.symbol),
|
|
roc_can::pattern::DestructType::Optional(_, _) => {
|
|
// if we reach this stage, the optional field is present
|
|
DestructType::Required(can_rd.symbol)
|
|
}
|
|
roc_can::pattern::DestructType::Guard(_, loc_pattern) => DestructType::Guard(
|
|
from_can_pattern_help(env, procs, layout_cache, &loc_pattern.value, assignments)?,
|
|
),
|
|
},
|
|
})
|
|
}
|
|
|
|
fn from_can_tuple_destruct<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_rd: &roc_can::pattern::TupleDestruct,
|
|
field_layout: InLayout<'a>,
|
|
assignments: &mut Vec<'a, (Symbol, Variable, roc_can::expr::Expr)>,
|
|
) -> Result<TupleDestruct<'a>, RuntimeError> {
|
|
Ok(TupleDestruct {
|
|
index: can_rd.destruct_index,
|
|
variable: can_rd.var,
|
|
layout: field_layout,
|
|
pat: from_can_pattern_help(env, procs, layout_cache, &can_rd.typ.1.value, assignments)?,
|
|
})
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn store_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_pat: &Pattern<'a>,
|
|
outer_symbol: Symbol,
|
|
stmt: Stmt<'a>,
|
|
) -> Stmt<'a> {
|
|
match store_pattern_help(env, procs, layout_cache, can_pat, outer_symbol, stmt) {
|
|
StorePattern::Productive(new) => new,
|
|
StorePattern::NotProductive(new) => new,
|
|
}
|
|
}
|
|
|
|
enum StorePattern<'a> {
|
|
/// we bound new symbols
|
|
Productive(Stmt<'a>),
|
|
/// no new symbols were bound in this pattern
|
|
NotProductive(Stmt<'a>),
|
|
}
|
|
|
|
/// It is crucial for correct RC insertion that we don't create dead variables!
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_pattern_help<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
can_pat: &Pattern<'a>,
|
|
outer_symbol: Symbol,
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
match can_pat {
|
|
Identifier(symbol) => {
|
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
|
}
|
|
Underscore => {
|
|
// do nothing
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
As(subpattern, symbol) => {
|
|
let stored_subpattern =
|
|
store_pattern_help(env, procs, layout_cache, subpattern, outer_symbol, stmt);
|
|
|
|
let mut stmt = match stored_subpattern {
|
|
StorePattern::Productive(stmt) => stmt,
|
|
StorePattern::NotProductive(stmt) => stmt,
|
|
};
|
|
|
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
|
|
|
return StorePattern::Productive(stmt);
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
NewtypeDestructure { arguments, .. } => match arguments.as_slice() {
|
|
[(pattern, _layout)] => {
|
|
return store_pattern_help(env, procs, layout_cache, pattern, outer_symbol, stmt);
|
|
}
|
|
_ => {
|
|
let mut fields = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
fields.extend(arguments.iter().map(|x| x.1));
|
|
|
|
let layout = layout_cache
|
|
.put_in_direct_no_semantic(LayoutRepr::struct_(fields.into_bump_slice()));
|
|
|
|
return store_newtype_pattern(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
outer_symbol,
|
|
layout,
|
|
arguments,
|
|
stmt,
|
|
);
|
|
}
|
|
},
|
|
AppliedTag {
|
|
arguments,
|
|
layout,
|
|
tag_id,
|
|
..
|
|
} => {
|
|
return store_tag_pattern(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
outer_symbol,
|
|
*layout,
|
|
arguments,
|
|
*tag_id,
|
|
stmt,
|
|
);
|
|
}
|
|
|
|
List {
|
|
arity,
|
|
list_layout,
|
|
element_layout,
|
|
elements,
|
|
opt_rest,
|
|
} => {
|
|
return store_list_pattern(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
outer_symbol,
|
|
*arity,
|
|
*list_layout,
|
|
*element_layout,
|
|
elements,
|
|
opt_rest,
|
|
stmt,
|
|
)
|
|
}
|
|
|
|
Voided { .. } => {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
|
|
OpaqueUnwrap { argument, .. } => {
|
|
let (pattern, _layout) = &**argument;
|
|
return store_pattern_help(env, procs, layout_cache, pattern, outer_symbol, stmt);
|
|
}
|
|
|
|
RecordDestructure(destructs, [_single_field]) => {
|
|
for destruct in destructs {
|
|
match &destruct.typ {
|
|
DestructType::Required(symbol) => {
|
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
|
}
|
|
DestructType::Guard(guard_pattern) => {
|
|
return store_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
guard_pattern,
|
|
outer_symbol,
|
|
stmt,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RecordDestructure(destructs, sorted_fields) => {
|
|
let mut is_productive = false;
|
|
for (index, destruct) in destructs.iter().enumerate().rev() {
|
|
match store_record_destruct(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
destruct,
|
|
index as u64,
|
|
outer_symbol,
|
|
sorted_fields,
|
|
stmt,
|
|
) {
|
|
StorePattern::Productive(new) => {
|
|
is_productive = true;
|
|
stmt = new;
|
|
}
|
|
StorePattern::NotProductive(new) => {
|
|
stmt = new;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !is_productive {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
}
|
|
|
|
TupleDestructure(destructs, [_single_field]) => {
|
|
if let Some(destruct) = destructs.first() {
|
|
return store_pattern_help(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
&destruct.pat,
|
|
outer_symbol,
|
|
stmt,
|
|
);
|
|
}
|
|
}
|
|
TupleDestructure(destructs, sorted_fields) => {
|
|
let mut is_productive = false;
|
|
for (index, destruct) in destructs.iter().enumerate().rev() {
|
|
match store_tuple_destruct(
|
|
env,
|
|
procs,
|
|
layout_cache,
|
|
destruct,
|
|
index as u64,
|
|
outer_symbol,
|
|
sorted_fields,
|
|
stmt,
|
|
) {
|
|
StorePattern::Productive(new) => {
|
|
is_productive = true;
|
|
stmt = new;
|
|
}
|
|
StorePattern::NotProductive(new) => {
|
|
stmt = new;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !is_productive {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
StorePattern::Productive(stmt)
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(crate) struct ListIndex(
|
|
/// Positive if we should index from the head, negative if we should index from the tail
|
|
/// 0 is lst[0]
|
|
/// -1 is lst[List.len lst - 1]
|
|
i64,
|
|
);
|
|
|
|
impl ListIndex {
|
|
pub fn from_pattern_index(index: usize, arity: ListArity) -> Self {
|
|
match arity {
|
|
ListArity::Exact(_) => Self(index as _),
|
|
ListArity::Slice(head, tail) => {
|
|
if index < head {
|
|
Self(index as _)
|
|
} else {
|
|
// Slice(head=2, tail=5)
|
|
//
|
|
// s t ... w y z x q
|
|
// 0 1 2 3 4 5 6 index
|
|
// 0 1 2 3 4 (index - head)
|
|
// 5 4 3 2 1 (tail - (index - head))
|
|
Self(-((tail - (index - head)) as i64))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) type Store<'a> = (Symbol, InLayout<'a>, Expr<'a>);
|
|
|
|
/// Builds the list index we should index into
|
|
#[must_use]
|
|
pub(crate) fn build_list_index_probe<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
list_sym: Symbol,
|
|
list_index: &ListIndex,
|
|
) -> (Symbol, impl DoubleEndedIterator<Item = Store<'a>>) {
|
|
let list_index = list_index.0;
|
|
let index_sym = env.unique_symbol();
|
|
|
|
let (opt_len_store, opt_offset_store, index_store) = if list_index >= 0 {
|
|
let index_expr = Expr::Literal(Literal::Int((list_index as i128).to_ne_bytes()));
|
|
|
|
let index_store = (index_sym, Layout::U64, index_expr);
|
|
|
|
(None, None, index_store)
|
|
} else {
|
|
let len_sym = env.unique_symbol();
|
|
let len_expr = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
op: LowLevel::ListLenU64,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([list_sym]),
|
|
});
|
|
|
|
let offset = list_index.abs();
|
|
let offset_sym = env.unique_symbol();
|
|
let offset_expr = Expr::Literal(Literal::Int((offset as i128).to_ne_bytes()));
|
|
|
|
let index_expr = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
op: LowLevel::NumSub,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([len_sym, offset_sym]),
|
|
});
|
|
|
|
let len_store = (len_sym, Layout::U64, len_expr);
|
|
let offset_store = (offset_sym, Layout::U64, offset_expr);
|
|
let index_store = (index_sym, Layout::U64, index_expr);
|
|
|
|
(Some(len_store), Some(offset_store), index_store)
|
|
};
|
|
|
|
let stores = (opt_len_store.into_iter())
|
|
.chain(opt_offset_store)
|
|
.chain([index_store]);
|
|
|
|
(index_sym, stores)
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_list_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
list_sym: Symbol,
|
|
list_arity: ListArity,
|
|
list_layout: InLayout<'a>,
|
|
element_layout: InLayout<'a>,
|
|
elements: &[Pattern<'a>],
|
|
opt_rest: &Option<(usize, Option<Symbol>)>,
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
let mut is_productive = false;
|
|
|
|
for (index, element) in elements.iter().enumerate().rev() {
|
|
let compute_element_load = |env: &mut Env<'a, '_>| {
|
|
let list_index = ListIndex::from_pattern_index(index, list_arity);
|
|
|
|
let (index_sym, needed_stores) = build_list_index_probe(env, list_sym, &list_index);
|
|
|
|
let load = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
op: LowLevel::ListGetUnsafe,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([list_sym, index_sym]),
|
|
});
|
|
|
|
(load, needed_stores)
|
|
};
|
|
|
|
let (store_loaded, needed_stores) = match element {
|
|
Identifier(symbol) => {
|
|
let (load, needed_stores) = compute_element_load(env);
|
|
|
|
// store immediately in the given symbol
|
|
(
|
|
Stmt::Let(*symbol, load, element_layout, env.arena.alloc(stmt)),
|
|
needed_stores,
|
|
)
|
|
}
|
|
Underscore
|
|
| IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {
|
|
// ignore
|
|
continue;
|
|
}
|
|
_ => {
|
|
// store the field in a symbol, and continue matching on it
|
|
let symbol = env.unique_symbol();
|
|
|
|
// first recurse, continuing to unpack symbol
|
|
match store_pattern_help(env, procs, layout_cache, element, symbol, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
stmt = new;
|
|
let (load, needed_stores) = compute_element_load(env);
|
|
|
|
// only if we bind one of its (sub)fields to a used name should we
|
|
// extract the field
|
|
(
|
|
Stmt::Let(symbol, load, element_layout, env.arena.alloc(stmt)),
|
|
needed_stores,
|
|
)
|
|
}
|
|
StorePattern::NotProductive(new) => {
|
|
// do nothing
|
|
stmt = new;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
is_productive = true;
|
|
|
|
stmt = store_loaded;
|
|
for (sym, lay, expr) in needed_stores.rev() {
|
|
stmt = Stmt::Let(sym, expr, lay, env.arena.alloc(stmt));
|
|
}
|
|
}
|
|
|
|
stmt = match store_list_rest(env, list_sym, list_arity, list_layout, opt_rest, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
is_productive = true;
|
|
new
|
|
}
|
|
StorePattern::NotProductive(new) => new,
|
|
};
|
|
|
|
if is_productive {
|
|
StorePattern::Productive(stmt)
|
|
} else {
|
|
StorePattern::NotProductive(stmt)
|
|
}
|
|
}
|
|
|
|
fn store_list_rest<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
list_sym: Symbol,
|
|
list_arity: ListArity,
|
|
list_layout: InLayout<'a>,
|
|
opt_rest: &Option<(usize, Option<Symbol>)>,
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
let mut is_productive = false;
|
|
|
|
if let Some((index, Some(rest_sym))) = opt_rest {
|
|
is_productive = true;
|
|
|
|
let total_dropped = list_arity.min_len();
|
|
let total_dropped_sym = env.unique_symbol();
|
|
let total_dropped_expr = Expr::Literal(Literal::Int((total_dropped as u128).to_ne_bytes()));
|
|
|
|
let list_len_sym = env.unique_symbol();
|
|
let list_len_expr = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
// Must use ListLenU64 here because we're using it with List.sublist,
|
|
// which takes U64s for start and len.
|
|
op: LowLevel::ListLenU64,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([list_sym]),
|
|
});
|
|
|
|
let rest_len_sym = env.unique_symbol();
|
|
let rest_len_expr = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
op: LowLevel::NumSub,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([list_len_sym, total_dropped_sym]),
|
|
});
|
|
|
|
let start_sym = env.unique_symbol();
|
|
let start_expr = Expr::Literal(Literal::Int((*index as u128).to_ne_bytes()));
|
|
|
|
let rest_expr = Expr::Call(Call {
|
|
call_type: CallType::LowLevel {
|
|
op: LowLevel::ListSublist,
|
|
update_mode: env.next_update_mode_id(),
|
|
},
|
|
arguments: env.arena.alloc([list_sym, start_sym, rest_len_sym]),
|
|
});
|
|
let needed_stores = [
|
|
(total_dropped_sym, total_dropped_expr, Layout::U64),
|
|
(list_len_sym, list_len_expr, Layout::U64),
|
|
(rest_len_sym, rest_len_expr, Layout::U64),
|
|
(start_sym, start_expr, Layout::U64),
|
|
(*rest_sym, rest_expr, list_layout),
|
|
];
|
|
for (sym, expr, lay) in needed_stores.into_iter().rev() {
|
|
stmt = Stmt::Let(sym, expr, lay, env.arena.alloc(stmt));
|
|
}
|
|
}
|
|
|
|
if is_productive {
|
|
StorePattern::Productive(stmt)
|
|
} else {
|
|
StorePattern::NotProductive(stmt)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_tag_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
structure: Symbol,
|
|
union_layout: UnionLayout<'a>,
|
|
arguments: &[(Pattern<'a>, InLayout<'a>)],
|
|
tag_id: TagIdIntType,
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
let mut is_productive = false;
|
|
|
|
for (index, (argument, arg_layout)) in arguments.iter().enumerate().rev() {
|
|
let mut arg_layout = *arg_layout;
|
|
|
|
if let LayoutRepr::RecursivePointer(_) = layout_cache.get_repr(arg_layout) {
|
|
// TODO(recursive-layouts): fix after disjoint rec ptrs
|
|
arg_layout = layout_cache.put_in_direct_no_semantic(LayoutRepr::Union(union_layout));
|
|
}
|
|
|
|
let load = Expr::UnionAtIndex {
|
|
index: index as u64,
|
|
structure,
|
|
tag_id,
|
|
union_layout,
|
|
};
|
|
|
|
match argument {
|
|
Identifier(symbol) => {
|
|
// store immediately in the given symbol
|
|
stmt = Stmt::Let(*symbol, load, arg_layout, env.arena.alloc(stmt));
|
|
is_productive = true;
|
|
}
|
|
Underscore => {
|
|
// ignore
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {}
|
|
_ => {
|
|
// store the field in a symbol, and continue matching on it
|
|
let symbol = env.unique_symbol();
|
|
|
|
// first recurse, continuing to unpack symbol
|
|
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
is_productive = true;
|
|
stmt = new;
|
|
// only if we bind one of its (sub)fields to a used name should we
|
|
// extract the field
|
|
stmt = Stmt::Let(symbol, load, arg_layout, env.arena.alloc(stmt));
|
|
}
|
|
StorePattern::NotProductive(new) => {
|
|
// do nothing
|
|
stmt = new;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if is_productive {
|
|
StorePattern::Productive(stmt)
|
|
} else {
|
|
StorePattern::NotProductive(stmt)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_newtype_pattern<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
structure: Symbol,
|
|
layout: InLayout<'a>,
|
|
arguments: &[(Pattern<'a>, InLayout<'a>)],
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
let mut is_productive = false;
|
|
|
|
for (_, layout) in arguments {
|
|
arg_layouts.push(*layout);
|
|
}
|
|
|
|
for (index, (argument, arg_layout)) in arguments.iter().enumerate().rev() {
|
|
let mut arg_layout = *arg_layout;
|
|
|
|
if let LayoutRepr::RecursivePointer(_) = layout_cache.get_repr(arg_layout) {
|
|
arg_layout = layout;
|
|
}
|
|
|
|
let load = Expr::StructAtIndex {
|
|
index: index as u64,
|
|
field_layouts: arg_layouts.clone().into_bump_slice(),
|
|
structure,
|
|
};
|
|
|
|
match argument {
|
|
Identifier(symbol) => {
|
|
stmt = Stmt::Let(*symbol, load, arg_layout, env.arena.alloc(stmt));
|
|
is_productive = true;
|
|
}
|
|
Underscore => {
|
|
// ignore
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {}
|
|
_ => {
|
|
// store the field in a symbol, and continue matching on it
|
|
let symbol = env.unique_symbol();
|
|
|
|
// first recurse, continuing to unpack symbol
|
|
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
is_productive = true;
|
|
stmt = new;
|
|
// only if we bind one of its (sub)fields to a used name should we
|
|
// extract the field
|
|
stmt = Stmt::Let(symbol, load, arg_layout, env.arena.alloc(stmt));
|
|
}
|
|
StorePattern::NotProductive(new) => {
|
|
// do nothing
|
|
stmt = new;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if is_productive {
|
|
StorePattern::Productive(stmt)
|
|
} else {
|
|
StorePattern::NotProductive(stmt)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_tuple_destruct<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
destruct: &TupleDestruct<'a>,
|
|
index: u64,
|
|
outer_symbol: Symbol,
|
|
sorted_fields: &'a [InLayout<'a>],
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
let load = Expr::StructAtIndex {
|
|
index,
|
|
field_layouts: sorted_fields,
|
|
structure: outer_symbol,
|
|
};
|
|
|
|
match &destruct.pat {
|
|
Identifier(symbol) => {
|
|
stmt = Stmt::Let(*symbol, load, destruct.layout, env.arena.alloc(stmt));
|
|
}
|
|
Underscore => {
|
|
// important that this is special-cased to do nothing: mono record patterns will extract all the
|
|
// fields, but those not bound in the source code are guarded with the underscore
|
|
// pattern. So given some record `{ x : a, y : b }`, a match
|
|
//
|
|
// { x } -> ...
|
|
//
|
|
// is actually
|
|
//
|
|
// { x, y: _ } -> ...
|
|
//
|
|
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
|
//
|
|
// This also happens with tuples, so when matching a tuple `(a, b, c)`,
|
|
// a pattern like `(x, y)` will be internally rewritten to `(x, y, _)`.
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
|
|
_ => {
|
|
let symbol = env.unique_symbol();
|
|
|
|
match store_pattern_help(env, procs, layout_cache, &destruct.pat, symbol, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
stmt = new;
|
|
stmt = Stmt::Let(symbol, load, destruct.layout, env.arena.alloc(stmt));
|
|
}
|
|
StorePattern::NotProductive(stmt) => return StorePattern::NotProductive(stmt),
|
|
}
|
|
}
|
|
}
|
|
|
|
StorePattern::Productive(stmt)
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn store_record_destruct<'a>(
|
|
env: &mut Env<'a, '_>,
|
|
procs: &mut Procs<'a>,
|
|
layout_cache: &mut LayoutCache<'a>,
|
|
destruct: &RecordDestruct<'a>,
|
|
index: u64,
|
|
outer_symbol: Symbol,
|
|
sorted_fields: &'a [InLayout<'a>],
|
|
mut stmt: Stmt<'a>,
|
|
) -> StorePattern<'a> {
|
|
use Pattern::*;
|
|
|
|
let load = Expr::StructAtIndex {
|
|
index,
|
|
field_layouts: sorted_fields,
|
|
structure: outer_symbol,
|
|
};
|
|
|
|
match &destruct.typ {
|
|
DestructType::Required(symbol) => {
|
|
stmt = Stmt::Let(*symbol, load, destruct.layout, env.arena.alloc(stmt));
|
|
}
|
|
DestructType::Guard(guard_pattern) => match &guard_pattern {
|
|
Identifier(symbol) => {
|
|
stmt = Stmt::Let(*symbol, load, destruct.layout, env.arena.alloc(stmt));
|
|
}
|
|
Underscore => {
|
|
// important that this is special-cased to do nothing: mono record patterns will extract all the
|
|
// fields, but those not bound in the source code are guarded with the underscore
|
|
// pattern. So given some record `{ x : a, y : b }`, a match
|
|
//
|
|
// { x } -> ...
|
|
//
|
|
// is actually
|
|
//
|
|
// { x, y: _ } -> ...
|
|
//
|
|
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
IntLiteral(_, _)
|
|
| FloatLiteral(_, _)
|
|
| DecimalLiteral(_)
|
|
| EnumLiteral { .. }
|
|
| BitLiteral { .. }
|
|
| StrLiteral(_) => {
|
|
return StorePattern::NotProductive(stmt);
|
|
}
|
|
|
|
_ => {
|
|
let symbol = env.unique_symbol();
|
|
|
|
match store_pattern_help(env, procs, layout_cache, guard_pattern, symbol, stmt) {
|
|
StorePattern::Productive(new) => {
|
|
stmt = new;
|
|
stmt = Stmt::Let(symbol, load, destruct.layout, env.arena.alloc(stmt));
|
|
}
|
|
StorePattern::NotProductive(stmt) => return StorePattern::NotProductive(stmt),
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
StorePattern::Productive(stmt)
|
|
}
|