diff --git a/src/erg_compiler/effectcheck.rs b/src/erg_compiler/effectcheck.rs index bd68dca3..63c7bfba 100644 --- a/src/erg_compiler/effectcheck.rs +++ b/src/erg_compiler/effectcheck.rs @@ -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("::"), 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(); diff --git a/src/erg_compiler/error.rs b/src/erg_compiler/error.rs index 9af75813..82c76f87 100644 --- a/src/erg_compiler/error.rs +++ b/src/erg_compiler/error.rs @@ -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>(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()) } } diff --git a/src/erg_compiler/ownercheck.rs b/src/erg_compiler/ownercheck.rs index d4a7b0ed..214e07a2 100644 --- a/src/erg_compiler/ownercheck.rs +++ b/src/erg_compiler/ownercheck.rs @@ -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, errs: OwnershipErrors, } @@ -33,18 +35,24 @@ pub struct OwnershipChecker { impl OwnershipChecker { pub fn new() -> Self { OwnershipChecker { - current_scope_name: Str::ever(""), + 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 { 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("::"), 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(""), - )); - } 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.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}"); } }