Shadowing of return values

Hi,

I’m trying out Odin and there is a behavior I don’t understand concerning variable shadowing.

If we have the following code, we have an error because the local variable i shadows the return parameter i:

foo :: proc() -> (i: i32) {
    i := 3 // error
    return
}

However, when the variable i is declared in a sub-scope, the error disappears:

foo :: proc() -> (i: i32) {
    {
        i := 83 // no error ?
        fmt.println(i)
    }
    return
}

The behavior is the same with local variables (it doesn’t have to be a return value).

Since Odin doesn’t allow shadowing in the same scope (unless for explicit parameter modification), I think it’s strange to allow it in sub-scopes. For me, shadowing should be, either allowed everywhere, or not at all (I personally prefer not). Is there a use case for this that I’m missing, or is it a bug?

Part 1:
You may not declare the same name twice in the same scope.

“:” declares a new variable.
“:=” declares and assigns a new variable.
“=” assigns an existing variable.
“::” declares and assigns a new constant.

You must be able to use any variable name in a new scope, because that’s what scope blocks are for. Imagine not being allowed to use the variable “i” anywhere in your code because it was used somewhere in a library.

Part 2:
A named output parameter is declared in the same scope block as the body. It has to be in order to be used.

Input parameters exist in their own scope block, and their names can be reused in the body. When using code like “i := i”, this is called shadowing. The “i” on the left hand side exists in a different (inner) scope from the “i” on the right hand (outer) side.

In your first example, you are declaring the variable “i” twice in the same scope - once in the output signature, and once in the body. This causes a naming conflict, and is an error.

In your second example, you declare “i” once in the output signature, and once in a separate inner scope block. No naming conflict.

Part 3:
Assignments in a program can be thought to exist in a stack of maps. When a name is referenced, the topmost map is checked, then the one under that, etc. until the name is found and correlated to a value. Each map can have many names, but any name can appear in each map only once, and correlates to a unique value.

When you call a proc, a new map is pushed onto the stack. When you open a new block, a new map is pushed onto the stack. When you close a block, that map is popped off the stack. When you exit a proc, that map is popped off the stack.

Thanks for the answer! Interestingly, this is the first time I have an issue with this so I was surprised. Now that I read your explanation, it makes sense.

1 Like