Merge pull request #133 from HigherOrderCO/feature/sc-359/don-t-allow-main-to-be-called-by-any-functions

[sc-359] Don't allow main to be called by any functions
This commit is contained in:
Nicolas Abril 2024-01-19 19:10:09 +01:00 committed by GitHub
commit 542383df16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 41 additions and 34 deletions

View File

@ -42,7 +42,7 @@ pub fn desugar_book(book: &mut Book, opt_level: OptimizationLevel) -> Result<Def
book.encode_strs()?;
book.encode_lists()?;
book.generate_scott_adts();
book.resolve_refs();
book.resolve_refs()?;
encode_pattern_matching(book)?;
book.normalize_native_matches()?;
book.check_unbound_vars()?;

View File

@ -6,52 +6,57 @@ impl Book {
/// Decides if names inside a term belong to a Var or to a Ref.
/// Precondition: Refs are encoded as vars.
/// Postcondition: Refs are encoded as refs, with the correct def id.
pub fn resolve_refs(&mut self) {
pub fn resolve_refs(&mut self) -> Result<(), String> {
for def in self.defs.values_mut() {
for rule in def.rules.iter_mut() {
rule.body.resolve_refs(&self.def_names);
rule.body.resolve_refs(&self.def_names)?;
}
}
Ok(())
}
}
impl Term {
pub fn resolve_refs(&mut self, def_names: &DefNames) {
pub fn resolve_refs(&mut self, def_names: &DefNames) -> Result<(), String> {
resolve_refs(self, def_names, &mut HashMap::new())
}
}
fn resolve_refs(term: &mut Term, def_names: &DefNames, scope: &mut HashMap<Name, usize>) {
fn resolve_refs(
term: &mut Term,
def_names: &DefNames,
scope: &mut HashMap<Name, usize>,
) -> Result<(), String> {
match term {
Term::Lam { nam, bod, .. } => {
push_scope(nam.clone(), scope);
resolve_refs(bod, def_names, scope);
resolve_refs(bod, def_names, scope)?;
pop_scope(nam.clone(), scope);
}
Term::Let { pat: Pattern::Var(nam), val, nxt } => {
resolve_refs(val, def_names, scope);
resolve_refs(val, def_names, scope)?;
push_scope(nam.clone(), scope);
resolve_refs(nxt, def_names, scope);
resolve_refs(nxt, def_names, scope)?;
pop_scope(nam.clone(), scope);
}
Term::Let { pat, val, nxt } => {
resolve_refs(val, def_names, scope);
resolve_refs(val, def_names, scope)?;
for nam in pat.names() {
push_scope(Some(nam.clone()), scope)
}
resolve_refs(nxt, def_names, scope);
resolve_refs(nxt, def_names, scope)?;
for nam in pat.names() {
pop_scope(Some(nam.clone()), scope)
}
}
Term::Dup { tag: _, fst, snd, val, nxt } => {
resolve_refs(val, def_names, scope);
resolve_refs(val, def_names, scope)?;
push_scope(fst.clone(), scope);
push_scope(snd.clone(), scope);
resolve_refs(nxt, def_names, scope);
resolve_refs(nxt, def_names, scope)?;
pop_scope(fst.clone(), scope);
pop_scope(snd.clone(), scope);
}
@ -59,27 +64,31 @@ fn resolve_refs(term: &mut Term, def_names: &DefNames, scope: &mut HashMap<Name,
// If variable not defined, we check if it's a ref and swap if it is.
Term::Var { nam } => {
if is_var_in_scope(nam.clone(), scope) {
if matches!(nam.0.as_ref(), DefNames::ENTRY_POINT | DefNames::HVM1_ENTRY_POINT) {
return Err("Main definition can't be referenced inside the program".to_string());
}
if let Some(def_id) = def_names.def_id(nam) {
*term = Term::Ref { def_id };
}
}
}
Term::Chn { bod, .. } => resolve_refs(bod, def_names, scope),
Term::Chn { bod, .. } => resolve_refs(bod, def_names, scope)?,
Term::App { fun: fst, arg: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Tup { fst, snd }
| Term::Opx { fst, snd, .. } => {
resolve_refs(fst, def_names, scope);
resolve_refs(snd, def_names, scope);
resolve_refs(fst, def_names, scope)?;
resolve_refs(snd, def_names, scope)?;
}
Term::Match { scrutinee, arms } => {
resolve_refs(scrutinee, def_names, scope);
resolve_refs(scrutinee, def_names, scope)?;
for (pat, term) in arms {
if let Pattern::Num(MatchNum::Succ(Some(nam))) = pat {
push_scope(nam.clone(), scope)
}
resolve_refs(term, def_names, scope);
resolve_refs(term, def_names, scope)?;
if let Pattern::Num(MatchNum::Succ(Some(nam))) = pat {
pop_scope(nam.clone(), scope)
@ -89,6 +98,8 @@ fn resolve_refs(term: &mut Term, def_names: &DefNames, scope: &mut HashMap<Name,
Term::List { .. } => unreachable!("Should have been desugared already"),
Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } | Term::Str { .. } | Term::Era => (),
}
Ok(())
}
fn push_scope(name: Option<Name>, scope: &mut HashMap<Name, usize>) {

View File

@ -15,10 +15,10 @@ impl Book {
let mut ref_id = def_id;
let mut is_ref_to_ref = false;
while let Term::Ref { def_id: next_ref_id } = &self.defs.get(ref_id).unwrap().rules[0].body {
if next_ref_id == def_id {
if next_ref_id == ref_id {
return Err(format!(
"Definition {} is a reference to itself",
self.def_names.name(def_id).unwrap()
self.def_names.name(ref_id).unwrap()
));
}
ref_id = next_ref_id;

View File

@ -170,7 +170,7 @@ fn encode_pattern_match() {
book.encode_strs()?;
book.encode_lists()?;
book.generate_scott_adts();
book.resolve_refs();
book.resolve_refs()?;
encode_pattern_matching(&mut book)?;
Ok(book.to_string())
})

View File

@ -0,0 +1,2 @@
Foo = Main
main = Foo

View File

@ -1 +1,2 @@
main = main
Foo = Foo
main = Foo

View File

@ -1 +0,0 @@
Main = λa Main

View File

@ -1 +0,0 @@
main = λa main

View File

@ -0,0 +1,5 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/ref_to_main.hvm
---
Main definition can't be referenced inside the program

View File

@ -2,4 +2,4 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/self_ref.hvm
---
Definition main is a reference to itself
Definition Foo is a reference to itself

View File

@ -1,5 +0,0 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/readback_hvm1_main.hvm
---
λ* Main

View File

@ -1,5 +0,0 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/readback_hvm2_main.hvm
---
λ* main