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

@@ -478,11 +478,7 @@ impl Compiler {
// simple interning - try to find a constant that is exactly equal to this one and just
// return its value instead
for (index, interned) in self.constants.iter().enumerate() {
if constant
.try_read()
.unwrap()
.equals(&*interned.try_read().unwrap())
{
if constant.equals(interned.as_ref()) {
return Ok(index as ConstantId);
}
}
@@ -665,7 +661,7 @@ impl StmtVisitor for Compiler {
// TODO - maybe this would be smarter to set up in the AST. I'm 99% sure that the last
// object created, if it were a function object, will be what we're assigning it to, but I
// want to be 100% sure instead of 99%.
let mut obj = self.constants.last_mut().unwrap().try_write().unwrap();
let obj = Arc::get_mut(self.constants.last_mut().unwrap()).unwrap();
if let Some(fun) = obj.as_any_mut().downcast_mut::<UserFunctionInst>() {
fun.set_name(Arc::new(name.to_string()));
}