M
Modular•2mo ago
ivellapillil

Preserving mutability when passing to function

How to make pointer passed to a function preserve/transmit its mutability?
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)

def change_value(p: Pointer[A]):
p[] = 15 # <- Error

change_value(ref_a)
ref_a[] = 15 # Works
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)

def change_value(p: Pointer[A]):
p[] = 15 # <- Error

change_value(ref_a)
ref_a[] = 15 # Works
Or is it a conscious constraint on Pointer and we should use UnsafePointer or Arc here?
25 Replies
Darkmatter
Darkmatter•2mo ago
That's a named parameter on Pointer that lets you be generic over mutability.
ivellapillil
ivellapillilOP•2mo ago
Thanks for the response. Actually I did try that, but didn't know what to put in the __origin_of .. MutableAnyOrigin does not work (as it seems to expect the origin of the a_instance itself. If the function is inside the same lexical scope of as_instance, then we can use that. But in case it is outside, it is not possible..
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value(p: Pointer[A]):
print(p[].val)

def change_value(p: Pointer[A, origin=__origin_of(a_instance)]): # Gives error ofc as a_instance is not in scope. What should go inside origin_of?
p[] = 15
...

def main():
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)
print_value(ref_a)
change_value(ref_a)
ref_a[] = 15
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value(p: Pointer[A]):
print(p[].val)

def change_value(p: Pointer[A, origin=__origin_of(a_instance)]): # Gives error ofc as a_instance is not in scope. What should go inside origin_of?
p[] = 15
...

def main():
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)
print_value(ref_a)
change_value(ref_a)
ref_a[] = 15
Darkmatter
Darkmatter•2mo ago
def change_value[is_mutable: Bool, //, origin: Origin[is_mutable]](p: Pointer[A, origin=origin):
p[] = 15
def change_value[is_mutable: Bool, //, origin: Origin[is_mutable]](p: Pointer[A, origin=origin):
p[] = 15
ivellapillil
ivellapillilOP•2mo ago
I am getting
error: 'Pointer' parameter 'origin' has 'Origin[?.value]' type, but value has type 'Origin[is_mutable]'
def change_value[is_mutable: Bool, //, origin: Origin[is_mutable]](p: Pointer[A, origin=origin]):
^~~~~~
error: 'Pointer' parameter 'origin' has 'Origin[?.value]' type, but value has type 'Origin[is_mutable]'
def change_value[is_mutable: Bool, //, origin: Origin[is_mutable]](p: Pointer[A, origin=origin]):
^~~~~~
mojo 2024.10.2406 (54f70ee8)
Darkmatter
Darkmatter•2mo ago
Give me a second, I missed .type after Origin[...], but there's other issues that came up once I pasted it into vscode.
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value[is_mutable: Bool, //, origin: Origin[is_mutable].type](p: Pointer[A, origin=origin]):
print(p[].val)

def change_value[origin: MutableOrigin](borrowed p: Pointer[A, origin=origin]):
p[] = 15

def main():
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)
print_value(ref_a)
change_value(ref_a)
ref_a[] = 15
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value[is_mutable: Bool, //, origin: Origin[is_mutable].type](p: Pointer[A, origin=origin]):
print(p[].val)

def change_value[origin: MutableOrigin](borrowed p: Pointer[A, origin=origin]):
p[] = 15

def main():
var a_instance = A(12)
var ref_a = Pointer.address_of(a_instance)
print_value(ref_a)
change_value(ref_a)
ref_a[] = 15
ivellapillil
ivellapillilOP•2mo ago
Thanks a lot! That works!
Darkmatter
Darkmatter•2mo ago
You don't need to use Pointer at all unless you have some other reason to:
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value(p: A):
print(p.val)

def change_value(inout p: A): # Gives error ofc as a_instance is not in scope. What should go inside origin_of?
p = 15

def main():
var a_instance = A(12)
print_value(a_instance)
change_value(a_instance)
a_instance = 15
struct A:
var val: Int
fn __init__(inout self, val: Int):
self.val = val

def print_value(p: A):
print(p.val)

def change_value(inout p: A): # Gives error ofc as a_instance is not in scope. What should go inside origin_of?
p = 15

def main():
var a_instance = A(12)
print_value(a_instance)
change_value(a_instance)
a_instance = 15
Most of the use of Pointer is to carry a reference inside of a struct. At least for now, myself and one other person are doing some PL work to see if Pointer can be the main reference type.
ivellapillil
ivellapillilOP•2mo ago
I used MutableAnyOrigin in place of MutableOrigin and it was not a type but a value... I got confused after that 🙂
Darkmatter
Darkmatter•2mo ago
MutableAnyOrigin means "The mutable origin that might access any memory value". It's effectively only useful inside of things like garbage collectors which can touch arbitrary memory.
ivellapillil
ivellapillilOP•2mo ago
Understood. Thanks!! Actually I wanted to explain the usage of Mojo's Pointer in my book.
Darkmatter
Darkmatter•2mo ago
Aha You are brave to write a book on a language this in flux.
ivellapillil
ivellapillilOP•2mo ago
I am writing what I am learning. I had to do quite a few refactoring though 😆
Darkmatter
Darkmatter•2mo ago
inout and borrowed are probably going away. What we have in their place is still the subject of debate.
ivellapillil
ivellapillilOP•2mo ago
Yes - I am actually following the conversations... Another set of refactorings on the way 🙂 I like your idea to keep reference a library type. Though also like Nick's syntax. A combination of both would be greaet
Darkmatter
Darkmatter•2mo ago
We're discussing it. Some of the semantics that Nick's proposal imposes for references have issues when you are doing multi-device or close to the metal work.
ivellapillil
ivellapillilOP•2mo ago
I am not a PL expert - but could ref and the others be just syntax sugar over a library defined ReferencePointer?
Darkmatter
Darkmatter•2mo ago
I think it's a good start, but ideally getting references moved out to library code would let people experiment without needing massive ecosystem changes every time. That was more or less the idea, except that there would be a way to change what the backing type is. So that you can have multi-node references (ex: top bits of the pointer are used to indicate which supercomputer node the data lives on) or signed pointers (for high security systems, you can add a cryptographic signing for pointers which go between devices the ensure that access is authenticated).
ivellapillil
ivellapillilOP•2mo ago
We can have that, and have a "default" referencepointer which is blessed with a syntax... The default one would be enough for 95% of cases... So being a Reference is a "trait" as you suggest, but there is a default implementation used by the compiler when we use ref
Darkmatter
Darkmatter•2mo ago
This would need to be a language level switch most likely, so you have to recompile the whole standard library. Or, it might be easier to just bless the type and let users modify the ReferencePointer type to their needs so long as they keep the API the same.
ivellapillil
ivellapillilOP•2mo ago
Or some kind of a preprocessor like:
use memory.ReferencePointer for ref
use memory.ReferencePointer for ref
But then we need to be super carefull that the different reference types interoporate seamlessly.
Darkmatter
Darkmatter•2mo ago
It probably needs to be a compiler switch.
ivellapillil
ivellapillilOP•2mo ago
Yes. Most likely But most developers would not have to worry.
Darkmatter
Darkmatter•2mo ago
Yes, the usecases for overriding what "ref [_]" maps to are very small.
Melody Daniel
Melody Daniel•2mo ago
Something I've always wondered... Is anybody from Modular involved in this discussion or will you guys come up with a proposal Chris will hopefully accept at some point?
Nick!
Nick!•2mo ago
Nobody from Modular has given feedback yet, so it's just me and Owen doing work right now as far as I know.
Want results from more Discord servers?
Add your server