Skip to content

Commit

Permalink
Merge pull request #215 from HigherOrderCO/bug/sc-453/don-t-stack-ove…
Browse files Browse the repository at this point in the history
…rflow-on-very-large-terms

[sc 453] don t stack overflow on very large terms
  • Loading branch information
LunaAmora authored Feb 29, 2024
2 parents 0b98642 + 86d3d4d commit 00f1a77
Show file tree
Hide file tree
Showing 17 changed files with 731,192 additions and 550 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ indexmap = "2.2.3"
interner = "0.2.1"
itertools = "0.11.0"
logos = "0.14.0"
stacker = "0.1"

[dev-dependencies]
insta = "1.34.0"
stdext = "0.3.1"
walkdir = "2.3.3"

[profile.test]
opt-level = 2
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub fn create_host(book: Arc<Book>, labels: Arc<Labels>, compile_opts: CompileOp
term.resugar_builtins();

readback_errors.extend(resugar_errs);
if let Term::Str { val } = term {
if let Term::Str { ref val } = term {
println!("{val}");
}
}
Expand Down
74 changes: 38 additions & 36 deletions src/term/check/ctrs_arities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,46 +75,48 @@ impl Pattern {

impl Term {
pub fn check_ctrs_arities(&self, arities: &HashMap<Name, usize>) -> Result<(), MatchErr> {
match self {
Term::Mat { args, rules } => {
for arg in args {
arg.check_ctrs_arities(arities)?;
}
for rule in rules {
for pat in &rule.pats {
pat.check_ctrs_arities(arities)?;
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
match self {
Term::Mat { args, rules } => {
for arg in args {
arg.check_ctrs_arities(arities)?;
}
for rule in rules {
for pat in &rule.pats {
pat.check_ctrs_arities(arities)?;
}
rule.body.check_ctrs_arities(arities)?;
}
rule.body.check_ctrs_arities(arities)?;
}
}
Term::Let { pat, val, nxt } => {
pat.check_ctrs_arities(arities)?;
val.check_ctrs_arities(arities)?;
nxt.check_ctrs_arities(arities)?;
}
Term::Let { pat, val, nxt } => {
pat.check_ctrs_arities(arities)?;
val.check_ctrs_arities(arities)?;
nxt.check_ctrs_arities(arities)?;
}

Term::Lst { els } => {
for el in els {
el.check_ctrs_arities(arities)?;
Term::Lst { els } => {
for el in els {
el.check_ctrs_arities(arities)?;
}
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
fst.check_ctrs_arities(arities)?;
snd.check_ctrs_arities(arities)?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_ctrs_arities(arities)?,
Term::Var { .. }
| Term::Lnk { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Ref { .. }
| Term::Era
| Term::Err => {}
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
fst.check_ctrs_arities(arities)?;
snd.check_ctrs_arities(arities)?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_ctrs_arities(arities)?,
Term::Var { .. }
| Term::Lnk { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Ref { .. }
| Term::Era
| Term::Err => {}
}
Ok(())
Ok(())
})
}
}
64 changes: 33 additions & 31 deletions src/term/check/match_arity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,43 @@ impl Definition {

impl Term {
pub fn check_match_arity(&self) -> Result<(), MatchErr> {
match self {
Term::Mat { args, rules } => {
let expected = args.len();
for rule in rules {
let found = rule.pats.len();
if found != expected {
return Err(MatchErr::ArityMismatch(found, expected));
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
match self {
Term::Mat { args, rules } => {
let expected = args.len();
for rule in rules {
let found = rule.pats.len();
if found != expected {
return Err(MatchErr::ArityMismatch(found, expected));
}
rule.body.check_match_arity()?;
}
rule.body.check_match_arity()?;
}
}

Term::Lst { els } => {
for el in els {
el.check_match_arity()?;
Term::Lst { els } => {
for el in els {
el.check_match_arity()?;
}
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. }
| Term::Let { val: fst, nxt: snd, .. } => {
fst.check_match_arity()?;
snd.check_match_arity()?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_match_arity()?,
Term::Var { .. }
| Term::Lnk { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Ref { .. }
| Term::Era
| Term::Err => {}
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. }
| Term::Let { val: fst, nxt: snd, .. } => {
fst.check_match_arity()?;
snd.check_match_arity()?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_match_arity()?,
Term::Var { .. }
| Term::Lnk { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Ref { .. }
| Term::Era
| Term::Err => {}
}
Ok(())
Ok(())
})
}
}
66 changes: 34 additions & 32 deletions src/term/check/unbound_pats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,41 +67,43 @@ impl Pattern {

impl Term {
pub fn check_unbound_pats(&self, is_ctr: &impl Fn(&Name) -> bool) -> Result<(), UnboundCtrErr> {
match self {
Term::Let { pat, val, nxt } => {
pat.check_unbounds(is_ctr)?;
val.check_unbound_pats(is_ctr)?;
nxt.check_unbound_pats(is_ctr)?;
}
Term::Mat { args, rules } => {
for arg in args {
arg.check_unbound_pats(is_ctr)?;
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
match self {
Term::Let { pat, val, nxt } => {
pat.check_unbounds(is_ctr)?;
val.check_unbound_pats(is_ctr)?;
nxt.check_unbound_pats(is_ctr)?;
}
for rule in rules {
for pat in &rule.pats {
pat.check_unbounds(is_ctr)?;
Term::Mat { args, rules } => {
for arg in args {
arg.check_unbound_pats(is_ctr)?;
}
for rule in rules {
for pat in &rule.pats {
pat.check_unbounds(is_ctr)?;
}
rule.body.check_unbound_pats(is_ctr)?;
}
rule.body.check_unbound_pats(is_ctr)?;
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
fst.check_unbound_pats(is_ctr)?;
snd.check_unbound_pats(is_ctr)?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_unbound_pats(is_ctr)?,
Term::Lst { .. } => unreachable!(),
Term::Var { .. }
| Term::Lnk { .. }
| Term::Ref { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Era
| Term::Err => (),
}
Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
fst.check_unbound_pats(is_ctr)?;
snd.check_unbound_pats(is_ctr)?;
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_unbound_pats(is_ctr)?,
Term::Lst { .. } => unreachable!(),
Term::Var { .. }
| Term::Lnk { .. }
| Term::Ref { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::Era
| Term::Err => (),
}
Ok(())
Ok(())
})
}
}
77 changes: 76 additions & 1 deletion src/term/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub struct Rule {
pub body: Term,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[derive(Debug, Default, PartialEq, Eq, Hash)]
pub enum Term {
Lam {
tag: Tag,
Expand Down Expand Up @@ -140,6 +140,81 @@ pub enum Term {
Err,
}

impl Clone for Term {
fn clone(&self) -> Self {
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || match self {
Self::Lam { tag, nam, bod } => Self::Lam { tag: tag.clone(), nam: nam.clone(), bod: bod.clone() },
Self::Var { nam } => Self::Var { nam: nam.clone() },
Self::Chn { tag, nam, bod } => Self::Chn { tag: tag.clone(), nam: nam.clone(), bod: bod.clone() },
Self::Lnk { nam } => Self::Lnk { nam: nam.clone() },
Self::Let { pat, val, nxt } => Self::Let { pat: pat.clone(), val: val.clone(), nxt: nxt.clone() },
Self::App { tag, fun, arg } => Self::App { tag: tag.clone(), fun: fun.clone(), arg: arg.clone() },
Self::Tup { fst, snd } => Self::Tup { fst: fst.clone(), snd: snd.clone() },
Self::Dup { tag, fst, snd, val, nxt } => {
Self::Dup { tag: tag.clone(), fst: fst.clone(), snd: snd.clone(), val: val.clone(), nxt: nxt.clone() }
}
Self::Sup { tag, fst, snd } => Self::Sup { tag: tag.clone(), fst: fst.clone(), snd: snd.clone() },
Self::Num { val } => Self::Num { val: val.clone() },
Self::Str { val } => Self::Str { val: val.clone() },
Self::Lst { els } => Self::Lst { els: els.clone() },
Self::Opx { op, fst, snd } => Self::Opx { op: op.clone(), fst: fst.clone(), snd: snd.clone() },
Self::Mat { args, rules } => Self::Mat { args: args.clone(), rules: rules.clone() },
Self::Ref { nam } => Self::Ref { nam: nam.clone() },
Self::Era => Self::Era,
Self::Err => Self::Err,
})
}
}

impl Drop for Term {
fn drop(&mut self) {
if matches!(self, Term::Era | Term::Err) {
return;
}

let mut stack = vec![];
self.take_children(&mut stack);

while let Some(mut term) = stack.pop() {
term.take_children(&mut stack)
}
}
}

impl Term {
fn take_children(&mut self, stack: &mut Vec<Term>) {
match self {
Term::Lam { bod, .. } | Term::Chn { bod, .. } => {
stack.push(std::mem::take(bod.as_mut()));
}
Term::Let { val: fst, nxt: snd, .. }
| Term::App { fun: fst, arg: snd, .. }
| Term::Tup { fst, snd }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
stack.push(std::mem::take(fst.as_mut()));
stack.push(std::mem::take(snd.as_mut()));
}
Term::Mat { args, rules } => {
for arg in std::mem::take(args).into_iter() {
stack.push(arg);
}

for Rule { body, .. } in std::mem::take(rules).into_iter() {
stack.push(body);
}
}
Term::Lst { els } => {
for el in std::mem::take(els).into_iter() {
stack.push(el);
}
}
_ => {}
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pattern {
Var(Option<Name>),
Expand Down
Loading

0 comments on commit 00f1a77

Please sign in to comment.