Recursive ownership checking

This commit is contained in:
Shunsuke Shibayama 2022-08-11 11:23:47 +09:00
parent 0c3f19f27a
commit 8bacb565d0
3 changed files with 65 additions and 28 deletions

View file

@ -7,10 +7,10 @@ use erg_common::color::{GREEN, RESET};
use erg_common::log; use erg_common::log;
use erg_common::traits::Stream; use erg_common::traits::Stream;
use crate::varinfo::Visibility;
use Visibility::*;
use crate::error::{EffectError, EffectErrors, EffectResult}; use crate::error::{EffectError, EffectErrors, EffectResult};
use crate::hir::{HIR, Expr, Def, Accessor, Signature}; use crate::hir::{HIR, Expr, Def, Accessor, Signature};
use crate::varinfo::Visibility;
use Visibility::*;
#[derive(Debug)] #[derive(Debug)]
pub struct SideEffectChecker { pub struct SideEffectChecker {
@ -54,13 +54,14 @@ impl SideEffectChecker {
} }
fn check_def(&mut self, def: &Def, allow_inner_effect: bool) { fn check_def(&mut self, def: &Def, allow_inner_effect: bool) {
self.path_stack.push(match &def.sig { let name_and_vis = match &def.sig {
Signature::Var(var) => Signature::Var(var) =>
// TODO: visibility // TODO: visibility
if let Some(name) = var.inspect() { (name.clone(), Private) } if let Some(name) = var.inspect() { (name.clone(), Private) }
else { (Str::ever("::<instant>"), Private) }, else { (Str::ever("::<instant>"), Private) },
Signature::Subr(subr) => (subr.name.inspect().clone(), Private), Signature::Subr(subr) => (subr.name.inspect().clone(), Private),
}); };
self.path_stack.push(name_and_vis);
// TODO: support raw identifier (``) // TODO: support raw identifier (``)
let is_procedural = def.sig.is_procedural(); let is_procedural = def.sig.is_procedural();
let is_subr = def.sig.is_subr(); let is_subr = def.sig.is_subr();

View file

@ -406,11 +406,11 @@ passed keyword args: {RED}{kw_args_len}{RESET}"),
), None), caused_by.into()) ), None), caused_by.into())
} }
pub fn move_error(name: &Str, name_loc: Location, moved_loc: Location, caused_by: Str) -> Self { pub fn move_error<S: Into<Str>>(name: &str, name_loc: Location, moved_loc: Location, caused_by: S) -> Self {
Self::new(ErrorCore::new(0, MoveError, name_loc, switch_lang!( Self::new(ErrorCore::new(0, MoveError, name_loc, switch_lang!(
format!("{RED}{name}{RESET} was moved in line {}", moved_loc.ln_begin().unwrap()), format!("{RED}{name}{RESET} was moved in line {}", moved_loc.ln_begin().unwrap()),
format!("{RED}{name}{RESET}{}行目ですでに移動されています", moved_loc.ln_begin().unwrap()) format!("{RED}{name}{RESET}{}行目ですでに移動されています", moved_loc.ln_begin().unwrap())
), None), caused_by) ), None), caused_by.into())
} }
} }

View file

@ -1,5 +1,5 @@
use erg_common::Str; use erg_common::Str;
use erg_common::{debug_power_assert, log}; use erg_common::{log};
use erg_common::color::{GREEN, RESET}; use erg_common::color::{GREEN, RESET};
use erg_common::dict::Dict; use erg_common::dict::Dict;
use erg_common::error::Location; use erg_common::error::Location;
@ -9,6 +9,8 @@ use erg_common::ty::{Type, ArgsOwnership, Ownership};
use crate::error::{OwnershipError, OwnershipErrors, OwnershipResult}; use crate::error::{OwnershipError, OwnershipErrors, OwnershipResult};
use crate::hir::{HIR, Def, Signature, Accessor, Block, Expr}; use crate::hir::{HIR, Def, Signature, Accessor, Block, Expr};
use crate::varinfo::Visibility;
use Visibility::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WrapperKind { pub enum WrapperKind {
@ -25,7 +27,7 @@ struct LocalVars {
#[derive(Debug)] #[derive(Debug)]
pub struct OwnershipChecker { pub struct OwnershipChecker {
current_scope_name: Str, path_stack: Vec<(Str, Visibility)>,
dict: Dict<Str, LocalVars>, dict: Dict<Str, LocalVars>,
errs: OwnershipErrors, errs: OwnershipErrors,
} }
@ -33,18 +35,24 @@ pub struct OwnershipChecker {
impl OwnershipChecker { impl OwnershipChecker {
pub fn new() -> Self { pub fn new() -> Self {
OwnershipChecker { OwnershipChecker {
current_scope_name: Str::ever("<dummy>"), path_stack: vec![],
dict: Dict::new(), dict: Dict::new(),
errs: OwnershipErrors::empty(), errs: OwnershipErrors::empty(),
} }
} }
fn full_path(&self) -> String {
self.path_stack.iter().fold(String::new(), |acc, (path, vis)| {
if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] }
})
}
// moveされた後の変数が使用されていないかチェックする // moveされた後の変数が使用されていないかチェックする
// ProceduralでないメソッドでRefMutが使われているかはSideEffectCheckerでチェックする // ProceduralでないメソッドでRefMutが使われているかはSideEffectCheckerでチェックする
pub fn check(mut self, hir: HIR) -> OwnershipResult<HIR> { pub fn check(mut self, hir: HIR) -> OwnershipResult<HIR> {
log!("{GREEN}[DEBUG] the ownership checking process has started.{RESET}"); log!("{GREEN}[DEBUG] the ownership checking process has started.{RESET}");
self.current_scope_name = hir.name.clone(); self.path_stack.push((hir.name.clone(), Private));
self.dict.insert(hir.name.clone(), LocalVars::default()); self.dict.insert(Str::from(self.full_path()), LocalVars::default());
for chunk in hir.module.iter() { for chunk in hir.module.iter() {
self.check_expr(chunk, Ownership::Owned); self.check_expr(chunk, Ownership::Owned);
} }
@ -62,29 +70,38 @@ impl OwnershipChecker {
} }
} }
// 参照を取るMethod Call中ならばreferenced: trueとなり、所有権は移動しない
fn check_expr(&mut self, expr: &Expr, ownership: Ownership) { fn check_expr(&mut self, expr: &Expr, ownership: Ownership) {
match expr { match expr {
Expr::Def(def) => { Expr::Def(def) => {
self.define(&def); self.define(&def);
let name_and_vis = match &def.sig {
Signature::Var(var) =>
// TODO: visibility
if let Some(name) = var.inspect() { (name.clone(), Private) }
else { (Str::ever("::<instant>"), Private) },
Signature::Subr(subr) => (subr.name.inspect().clone(), Private),
};
self.path_stack.push(name_and_vis);
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
self.check_block(&def.body.block); self.check_block(&def.body.block);
self.path_stack.pop();
}, },
Expr::Accessor(Accessor::Local(local)) => { Expr::Accessor(Accessor::Local(local)) => {
// TODO: スコープを再帰的にチェックする for n in 0..self.path_stack.len() {
if let Some(moved_loc) = self.current_scope().dropped_vars.get(local.inspect()) { if let Some(moved_loc) = self.nth_outer_scope(n).dropped_vars.get(local.inspect()) {
let moved_loc = *moved_loc; let moved_loc = *moved_loc;
self.errs.push(OwnershipError::move_error( self.errs.push(OwnershipError::move_error(
local.inspect(), local.inspect(),
local.loc(), local.loc(),
moved_loc, moved_loc,
Str::from("<dummy>"), self.full_path(),
)); ));
} else {
if expr.ref_t().is_mut() && ownership.is_owned() {
log!("dropped: {}", local.inspect());
self.drop(local.inspect(), expr.loc());
} }
} }
if expr.ref_t().is_mut() && ownership.is_owned() {
log!("dropped: {}", local.inspect());
self.drop(local.inspect(), expr.loc());
}
}, },
Expr::Accessor(Accessor::Attr(a)) => { Expr::Accessor(Accessor::Attr(a)) => {
if a.ref_t() != &Type::ASTOmitted { todo!("{a}: {}", a.ref_t()) } if a.ref_t() != &Type::ASTOmitted { todo!("{a}: {}", a.ref_t()) }
@ -145,7 +162,11 @@ impl OwnershipChecker {
}, },
// TODO: capturing // TODO: capturing
Expr::Lambda(lambda) => { Expr::Lambda(lambda) => {
let name_and_vis = (Str::from(format!("<lambda_{}>", lambda.id)), Private);
self.path_stack.push(name_and_vis);
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
self.check_block(&lambda.body); self.check_block(&lambda.body);
self.path_stack.pop();
}, },
_ => {}, _ => {},
} }
@ -154,7 +175,17 @@ impl OwnershipChecker {
/// TODO: このメソッドを呼ぶとき、スコープを再帰的に検索する /// TODO: このメソッドを呼ぶとき、スコープを再帰的に検索する
#[inline] #[inline]
fn current_scope(&mut self) -> &mut LocalVars { fn current_scope(&mut self) -> &mut LocalVars {
self.dict.get_mut(&self.current_scope_name).unwrap() self.dict.get_mut(&self.full_path()[..]).unwrap()
}
#[inline]
fn nth_outer_scope(&mut self, n: usize) -> &mut LocalVars {
let path = self.path_stack.iter()
.take(self.path_stack.len() - n)
.fold(String::new(), |acc, (path, vis)| {
if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] }
});
self.dict.get_mut(&path[..]).unwrap()
} }
fn define(&mut self, def: &Def) { fn define(&mut self, def: &Def) {
@ -171,7 +202,12 @@ impl OwnershipChecker {
} }
fn drop(&mut self, name: &Str, moved_loc: Location) { fn drop(&mut self, name: &Str, moved_loc: Location) {
debug_power_assert!(self.current_scope().alive_vars.remove(name), ==, true); for n in 0..self.path_stack.len() {
self.current_scope().dropped_vars.insert(name.clone(), moved_loc); if self.nth_outer_scope(n).alive_vars.remove(name) {
self.nth_outer_scope(n).dropped_vars.insert(name.clone(), moved_loc);
return
}
}
panic!("variable not found: {name}");
} }
} }