mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 21:01:10 +00:00
Recursive ownership checking
This commit is contained in:
parent
0c3f19f27a
commit
8bacb565d0
3 changed files with 65 additions and 28 deletions
|
@ -7,10 +7,10 @@ use erg_common::color::{GREEN, RESET};
|
|||
use erg_common::log;
|
||||
use erg_common::traits::Stream;
|
||||
|
||||
use crate::varinfo::Visibility;
|
||||
use Visibility::*;
|
||||
use crate::error::{EffectError, EffectErrors, EffectResult};
|
||||
use crate::hir::{HIR, Expr, Def, Accessor, Signature};
|
||||
use crate::varinfo::Visibility;
|
||||
use Visibility::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SideEffectChecker {
|
||||
|
@ -54,13 +54,14 @@ impl SideEffectChecker {
|
|||
}
|
||||
|
||||
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) =>
|
||||
// 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);
|
||||
// TODO: support raw identifier (``)
|
||||
let is_procedural = def.sig.is_procedural();
|
||||
let is_subr = def.sig.is_subr();
|
||||
|
|
|
@ -406,11 +406,11 @@ passed keyword args: {RED}{kw_args_len}{RESET}"),
|
|||
), 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!(
|
||||
format!("{RED}{name}{RESET} was moved in line {}", moved_loc.ln_begin().unwrap()),
|
||||
format!("{RED}{name}{RESET}は{}行目ですでに移動されています", moved_loc.ln_begin().unwrap())
|
||||
), None), caused_by)
|
||||
), None), caused_by.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use erg_common::Str;
|
||||
use erg_common::{debug_power_assert, log};
|
||||
use erg_common::{log};
|
||||
use erg_common::color::{GREEN, RESET};
|
||||
use erg_common::dict::Dict;
|
||||
use erg_common::error::Location;
|
||||
|
@ -9,6 +9,8 @@ use erg_common::ty::{Type, ArgsOwnership, Ownership};
|
|||
|
||||
use crate::error::{OwnershipError, OwnershipErrors, OwnershipResult};
|
||||
use crate::hir::{HIR, Def, Signature, Accessor, Block, Expr};
|
||||
use crate::varinfo::Visibility;
|
||||
use Visibility::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum WrapperKind {
|
||||
|
@ -25,7 +27,7 @@ struct LocalVars {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct OwnershipChecker {
|
||||
current_scope_name: Str,
|
||||
path_stack: Vec<(Str, Visibility)>,
|
||||
dict: Dict<Str, LocalVars>,
|
||||
errs: OwnershipErrors,
|
||||
}
|
||||
|
@ -33,18 +35,24 @@ pub struct OwnershipChecker {
|
|||
impl OwnershipChecker {
|
||||
pub fn new() -> Self {
|
||||
OwnershipChecker {
|
||||
current_scope_name: Str::ever("<dummy>"),
|
||||
path_stack: vec![],
|
||||
dict: Dict::new(),
|
||||
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された後の変数が使用されていないかチェックする
|
||||
// ProceduralでないメソッドでRefMutが使われているかはSideEffectCheckerでチェックする
|
||||
pub fn check(mut self, hir: HIR) -> OwnershipResult<HIR> {
|
||||
log!("{GREEN}[DEBUG] the ownership checking process has started.{RESET}");
|
||||
self.current_scope_name = hir.name.clone();
|
||||
self.dict.insert(hir.name.clone(), LocalVars::default());
|
||||
self.path_stack.push((hir.name.clone(), Private));
|
||||
self.dict.insert(Str::from(self.full_path()), LocalVars::default());
|
||||
for chunk in hir.module.iter() {
|
||||
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) {
|
||||
match expr {
|
||||
Expr::Def(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.path_stack.pop();
|
||||
},
|
||||
Expr::Accessor(Accessor::Local(local)) => {
|
||||
// TODO: スコープを再帰的にチェックする
|
||||
if let Some(moved_loc) = self.current_scope().dropped_vars.get(local.inspect()) {
|
||||
let moved_loc = *moved_loc;
|
||||
self.errs.push(OwnershipError::move_error(
|
||||
local.inspect(),
|
||||
local.loc(),
|
||||
moved_loc,
|
||||
Str::from("<dummy>"),
|
||||
));
|
||||
} else {
|
||||
if expr.ref_t().is_mut() && ownership.is_owned() {
|
||||
log!("dropped: {}", local.inspect());
|
||||
self.drop(local.inspect(), expr.loc());
|
||||
for n in 0..self.path_stack.len() {
|
||||
if let Some(moved_loc) = self.nth_outer_scope(n).dropped_vars.get(local.inspect()) {
|
||||
let moved_loc = *moved_loc;
|
||||
self.errs.push(OwnershipError::move_error(
|
||||
local.inspect(),
|
||||
local.loc(),
|
||||
moved_loc,
|
||||
self.full_path(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if expr.ref_t().is_mut() && ownership.is_owned() {
|
||||
log!("dropped: {}", local.inspect());
|
||||
self.drop(local.inspect(), expr.loc());
|
||||
}
|
||||
},
|
||||
Expr::Accessor(Accessor::Attr(a)) => {
|
||||
if a.ref_t() != &Type::ASTOmitted { todo!("{a}: {}", a.ref_t()) }
|
||||
|
@ -145,7 +162,11 @@ impl OwnershipChecker {
|
|||
},
|
||||
// TODO: capturing
|
||||
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.path_stack.pop();
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
@ -154,7 +175,17 @@ impl OwnershipChecker {
|
|||
/// TODO: このメソッドを呼ぶとき、スコープを再帰的に検索する
|
||||
#[inline]
|
||||
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) {
|
||||
|
@ -171,7 +202,12 @@ impl OwnershipChecker {
|
|||
}
|
||||
|
||||
fn drop(&mut self, name: &Str, moved_loc: Location) {
|
||||
debug_power_assert!(self.current_scope().alive_vars.remove(name), ==, true);
|
||||
self.current_scope().dropped_vars.insert(name.clone(), moved_loc);
|
||||
for n in 0..self.path_stack.len() {
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue