WIP: move mutability to be internal to the object instead of the pointer

I'm not super happy with this. But, the RwLock has been moved to the
`BaseObjInst::attrs` member. Although this is not exactly how it appears
in code, it basically does this:

    type Ptr<T> = Arc<RwLock<T>>;

    struct BaseObjInst {
        attr: HashMap<String, Ptr<dyn Obj>>,
        // etc
    }

becomes

    type Ptr<T> = Arc<T>;

    struct BaseObjInst {
        attr: RwLock<HashMap<String, ObjP>>,
        // etc
    }

This makes things a lot more ergonomic (don't have to use try_read() and
try_write() everywhere), but it also eliminates compile-time errors that
would catch mutability errors. This is currently rearing its ugly head
when initializing the typesystem, since `Type` needs to hold a circular
reference itself (which it already shouldn't be doing since it's a
reference-counted pointer!). Currently, all tests are failing because of
this limitation.

There are a couple of ways around this limitation.

The first solution would be just copying  all of the object
instantiation code into the `init_types` function and avoid calling
`some_base_type.instantiate()`. This would probably be literal
copy-pasting, or maybe an (ugly) macro, and probably a nightmare to
maintain long-term. I don't like this option, but it would make
everything "just work" with reference-counted pointers.

The second solution would be to write our own garbage collector, which
would allow for circular references and (hypothetically) mutably
updating these references. This is something that I am looking into,
because I really want a RefCell that you can pass around in a more
ergonomic way.

I think the fundamental error that I'm running into is trying to borrow
the same value multiple times mutably, which you *really* shouldn't be
doing. I believe I need to write better code and does the same thing.

The only unsolved problem is circular references. This is not a problem
right now because I'm not writing code that has circular references
besides the base typesystem (which is not a problem because they need to
live the entire lifetime of the program), but it will be a latent
problem until it gets fixed.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-09-22 20:40:15 -07:00
parent 16f3dc960c
commit 24b06851c7
5 changed files with 76 additions and 94 deletions

View File

@@ -233,7 +233,7 @@ impl Vm {
let name =
with_obj_downcast(name_obj, |name: &StrInst| Arc::clone(&name.str_value()));
let owner = self.pop();
let value = owner.try_read().unwrap().get_attr(&name);
let value = owner.get_attr(&name);
if let Some(value) = value {
self.push(value);
} else {
@@ -243,7 +243,7 @@ impl Vm {
todo!(
"throw an error because we couldn't read attr '{}' on '{}'",
name,
owner.try_read().unwrap(),
owner,
);
}
}
@@ -254,8 +254,7 @@ impl Vm {
let value = self.pop();
let target = self.pop();
let mut target_ptr = target.try_write().unwrap();
target_ptr.set_attr(&name, value);
target.set_attr(&name, value);
}
Op::Jump(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
@@ -265,14 +264,14 @@ impl Vm {
Op::JumpFalse(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
let value = self.peek();
if !value.try_read().unwrap().is_truthy() {
if !value.is_truthy() {
self.set_ip((base + offset) as usize);
}
}
Op::JumpTrue(offset) => {
let base = (self.ip() - 1) as JumpOpArg;
let value = self.peek();
if value.try_read().unwrap().is_truthy() {
if value.is_truthy() {
self.set_ip((base + offset) as usize);
}
}
@@ -280,7 +279,6 @@ impl Vm {
let argc = argc as usize;
let index = self.stack.len() - argc - 1;
let fun_ptr = Ptr::clone(&self.stack[index]);
let fun_ptr = fun_ptr.try_read().unwrap();
let arity = if let Some(arity) = fun_ptr.arity() {
arity as usize