Ruschm icon indicating copy to clipboard operation
Ruschm copied to clipboard

User procedure will leak memory

Open CurryPseudo opened this issue 5 years ago • 5 comments

#[derive(Clone, Debug, PartialEq)]
pub struct StandardEnv<R: RealNumberInternalTrait> {
    parent: Option<Rc<StandardEnv<R>>>,
    definitions: RefCell<HashMap<String, Value<R, StandardEnv<R>>>>,
}
...
#[derive(Debug, Clone, PartialEq)]
pub enum Procedure<R: RealNumberInternalTrait, E: IEnvironment<R>> {
    User(SchemeProcedure, Rc<E>),
    Buildin(BuildinProcedure<R, E>),
}

while user procedure carrying an rc environment, then the environment having the definitions that contains the procedure, the rc environment will never release

CurryPseudo avatar Nov 04 '20 07:11 CurryPseudo

A closure will be released when out leave the scope using it, thus the referenced environment would be released.

(lambda () ; env1
    (define foo 
        (lambda () ; env2 with var `count`
            (define count 0)
            (lambda () (+ count 1) count)
        ); env2 rc count-1 = 1
    )
    (define bar (foo)) ; make env2 live with env1
    (bar) ;1
    (bar) ;2
)
; releasing env1 will cause releasing foo, which will release env2 


Danielmelody avatar Nov 04 '20 07:11 Danielmelody

; an root env1, rc count = 1 (interpreter::env)
(define foo
    (lambda ()
    )
); an user procedure (foo's AST, env1), clone the env1 rc , rc count + 1 = 2, then move into env1::definitions
; interpreter destroy, env1 rc count -1 = 1
; env1 leak

CurryPseudo avatar Nov 04 '20 08:11 CurryPseudo

impl<R: RealNumberInternalTrait, E: IEnvironment<R>> Interpreter<R, E> {
...
        pub fn eval_expression(expression: &Expression, env: &Rc<E>) -> Result<Value<R, E>> {
        ...
            ExpressionBody::Procedure(scheme) => {
                Value::Procedure(Procedure::User(scheme.clone(), env.clone()))
            }
        ...
        }
    ...
}

CurryPseudo avatar Nov 04 '20 08:11 CurryPseudo

I see it, seems a complete closure cannot be implemented without gc.

Danielmelody avatar Nov 04 '20 09:11 Danielmelody

If consider unsafe closure, like in C++, this problem can be solved by some unsafe code representing capture by reference. As a interpreter using gc is the safest way. But if consider compiling to LLVM IR, we will end up with 3 situations:

  • unsafe scheme
  • native code with a native gc
  • rust-like lifetime checker to reject unsafe use of closure

Danielmelody avatar Nov 04 '20 09:11 Danielmelody