Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling inject_at with an element already in the array copies the wrong value #4604

Open
jace0x21 opened this issue Dec 21, 2024 · 2 comments

Comments

@jace0x21
Copy link

Context

package main

import "core:fmt"

// Need struct with at least 3 ints. Issue didn't manifest with 2.
Foo :: struct {a, x, y, z: int}

main :: proc() {
    foo_arr := [dynamic]Foo{Foo{1, 1, 1, 1}, Foo{2, 2, 2, 2}, Foo{3, 3, 3, 3}}
    reserve(&foo_arr, 4)
    fmt.printf("%v\n", foo_arr)
    inject_at(&foo_arr, 0, foo_arr[2])
    fmt.printf("%v\n", foo_arr)
}

The above program prints

[Foo{a = 1, x = 1, y = 1, z = 1}, Foo{a = 2, x = 2, y = 2, z = 2}, Foo{a = 3, x = 3, y = 3, z = 3}]
[Foo{a = 2, x = 2, y = 2, z = 2}, Foo{a = 1, x = 1, y = 1, z = 1}, Foo{a = 2, x = 2, y = 2, z = 2}, Foo{a = 3, x = 3, y = 3, z = 3}]
  • Operating System & Odin Version:
    Odin: dev-2024-12:0a29d36aa
    OS: macOS Sonoma 14.4.1 (build 23E224, kernel 23.4.0)
    CPU: Apple M2
    RAM: 24576 MiB
    Backend: LLVM 18.1.8

Expected Behavior

I expect the output to be:

[Foo{a = 1, x = 1, y = 1, z = 1}, Foo{a = 2, x = 2, y = 2, z = 2}, Foo{a = 3, x = 3, y = 3, z = 3}]
[Foo{a = 3, x = 3, y = 3, z = 3}, Foo{a = 1, x = 1, y = 1, z = 1}, Foo{a = 2, x = 2, y = 2, z = 2}, Foo{a = 3, x = 3, y = 3, z = 3}]

Current Behavior

inject_at will move down all elements in the array and then the arg pointer will be pointing at the wrong element

Failure Information (for bugs)

You need a struct with more than two fields for this bug to manifest. As I understand it, this is because small structs don't need to be passed by pointer.

Steps to Reproduce

Please provide detailed steps for reproducing the issue.

  1. Compile the above program
  2. Run it
@jace0x21
Copy link
Author

jace0x21 commented Dec 21, 2024

--- a/base/runtime/core_builtin.odin
+++ b/base/runtime/core_builtin.odin
@@ -655,6 +655,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
        m :: 1
        new_size := n + m
 
+       arg := arg
        resize(array, new_size, loc) or_return
        when size_of(E) != 0 {
                copy(array[index + m:], array[index:])

resolves the issue by making an explicit copy but then you'd probably want to do that for every function where we might change a pointer's value before copying that pointer's value. I wonder if there is a better solution. Instead of using a pointer to the heap for large struct values, I wonder if the value should always be copied to the stack.

@jace0x21
Copy link
Author

So it looks like this was likely a misunderstanding on my part. In this case foo_arr[2] is treated as a slice, which is passed as a pointer according to Odin's documentation. The mistake was thinking it would be treated as the Foo struct being passed by value as opposed to a pointer to the Foo struct.

In this case, it's probably best to explicitly copy the struct before passing it to a function:
element := foo_arr[2]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant