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•4w ago
That's a named parameter on Pointer that lets you be generic over mutability.
ivellapillil
ivellapillilOP•4w 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•4w 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•4w 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•4w 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•4w ago
Thanks a lot! That works!
Darkmatter
Darkmatter•4w 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•4w ago
I used MutableAnyOrigin in place of MutableOrigin and it was not a type but a value... I got confused after that 🙂
Darkmatter
Darkmatter•4w 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•4w ago
Understood. Thanks!! Actually I wanted to explain the usage of Mojo's Pointer in my book.
Darkmatter
Darkmatter•4w ago
Aha You are brave to write a book on a language this in flux.
ivellapillil
ivellapillilOP•4w ago
I am writing what I am learning. I had to do quite a few refactoring though 😆
Darkmatter
Darkmatter•4w ago
inout and borrowed are probably going away. What we have in their place is still the subject of debate.
ivellapillil
ivellapillilOP•4w 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•4w 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•4w ago
I am not a PL expert - but could ref and the others be just syntax sugar over a library defined ReferencePointer?
Darkmatter
Darkmatter•4w 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•4w 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•4w 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•4w 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•4w ago
It probably needs to be a compiler switch.
ivellapillil
ivellapillilOP•4w ago
Yes. Most likely But most developers would not have to worry.
Darkmatter
Darkmatter•4w ago
Yes, the usecases for overriding what "ref [_]" maps to are very small.
Melody Daniel
Melody Daniel•4w 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!•4w 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