v icon indicating copy to clipboard operation
v copied to clipboard

modifying a struct pointer by passing it as mut to a function

Open thomas-mangin opened this issue 2 years ago • 0 comments

Describe the bug

V will modify a struct pointer if passed as mut to a function.

Expected Behavior

The value does not change. The current behaviour can be explained that mut will change the underlying object but when referencing a struct variable, the "intuitive" perception would be that the address of the object will be used and mut only expresses that the underlying code is capable of changing the value.

Current Behavior

The value surprisingly changes. While the structure is declared [heap], I would have expected t.root to be created as a copy on the stack, as the function should not modify t.

It would seem V does not detect that there is some indirection with the data passed.

Reproduction Steps

module main

[heap]
struct Node {
mut:
	children []&Node
}

[heap]
struct Tree {
mut:
	root &Node
}

// // Does not work, hence why we have to pass node as mut

// // bug-ptr.v:17:16: error: `node` is immutable, cannot have a mutable reference to an immutable object
// //    15 | // Does not work, hence why we have to pass node as mut
// //    16 | fn parse_nope(node &Node) &Node {
// //    17 |     mut inside := node
// //       |                   ~~~~
// //    18 |     for {
// //    19 |         if inside.children.len == 0 {

// fn parse_nope(node &Node) &Node {
// 	mut inside := node
// 	for {
// 		if inside.children.len == 0 {
// 			return inside
// 		}
// 		inside = inside.children[0]
// 	}
// 	return inside
// }

// Adding mut instead of and passign t.root
// had the surprising effect of modifying t
fn parse_weird(mut node Node) &Node {
	for {
		if node.children.len == 0 {
			return node
		}
		node = node.children[0]
	}
	return node
}

// work around: use another mut copy
fn parse_ok(mut node Node) &Node {
	mut inside := &Node{}
	inside = node
	for {
		if inside.children.len == 0 {
			return inside
		}
		inside = inside.children[0]
	}
	return inside
}

fn main () {
	mut n1 := &Node{}
	mut t := &Tree { root: n1 }

	mut n2 := &Node{}
	t.root.children << n2
	mut n3 := &Node{}
	t.root.children[0].children << n3

	root_str := t.root.str()
	n3_str := n3.str()
	_ := n3_str

	// found0 = parse_nope(t.root)
	// assert found0.str() == n3_str
	// assert found0.str() != root_str

	found0 := parse_ok(mut t.root)
	assert found0.str() == n3_str
	assert found0.str() != root_str

	found1 := parse_weird(mut t.root)
	// pass: return n3 correctly
	assert found1.str() == n3_str
	// fail: I would never have expected to have modified t.root !
	assert t.root.str() == root_str
}

Possible Solution

explicitly copying the mut value into another mut value before using it, which is what I would have expected the callee to do.

Additional Information/Context

No response

V version

V 0.3.3 f08b882

Environment details (OS name and version, etc.)

V full version: V 0.3.3 f08b882
OS: macos, macOS, 13.2.1, 22D68
Processor: 10 cpus, 64bit, little endian, Apple M1 Max

getwd: /Users/thomas/Code/github.com/ze-core/network/bugs
vexe: /Users/thomas/Unix/local/v/v
vexe mtime: 2023-03-26 22:13:15

vroot: OK, value: /Users/thomas/Unix/local/v
VMODULES: NOT writable, value: /Users/thomas/Unix/data/v/modules
VTMP: OK, value: /tmp/v_501

Git version: git version 2.39.2
Git vroot status: weekly.2023.12-36-gf08b8822-dirty (1 commit(s) behind V master)
.git/config present: true

CC version: Apple clang version 14.0.0 (clang-1400.0.29.202)
thirdparty/tcc status: thirdparty-macos-arm64 a668e5a0

thomas-mangin avatar Mar 27 '23 09:03 thomas-mangin