Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User procedure will leak memory #54

Open
CurryPseudo opened this issue Nov 4, 2020 · 5 comments
Open

User procedure will leak memory #54

CurryPseudo opened this issue Nov 4, 2020 · 5 comments

Comments

@CurryPseudo
Copy link
Collaborator

#[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

@Danielmelody
Copy link
Owner

Danielmelody commented Nov 4, 2020

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 

@CurryPseudo
Copy link
Collaborator Author

; 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
Copy link
Collaborator Author

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()))
            }
        ...
        }
    ...
}

@Danielmelody
Copy link
Owner

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

@Danielmelody
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants