Why does the Go compiler do this optimization - go

package main
import (
"fmt"
)
func main() {
a := new(struct{})
b := new(struct{})
println(a, b, a==b)
c := new(struct{})
d := new(struct{})
fmt.Println(c, d, c==d)
}
output:
0xc000107f37 0xc000107f37 false
&{} &{} true
And, I will get different results after disabling compiler optimization.
0xc000087e4e 0xc000087e4e true
&{} &{} true
Why does the Go compiler do this optimization?
See [this line in the runtime source code ]
(https://github.com/golang/go/blob/6317adeed7815ad335e2e97c463a7c3c4d82fc19/src/runtime/malloc.go#L586)
if size == 0 {
return unsafe.Pointer(&zerobase)
}
I think that since all zero-size variables will be allocated with zerobase, it should print true instead of false.

Related

Printing variables changes the previous code results [duplicate]

This question already has answers here:
why struct arrays comparing has different result
(1 answer)
Addresses of slices of empty structs
(2 answers)
Closed 2 months ago.
I have the following code:
package main
import "fmt"
type MyStruct struct {
}
func main() {
a := &MyStruct{}
b := &MyStruct{}
fmt.Println(a == b)
fmt.Println(*a == *b)
}
Which as expected outputs
false
true
But, if I add two Print statements at the end like this:
package main
import "fmt"
type MyStruct struct {
}
func main() {
a := &MyStruct{}
b := &MyStruct{}
fmt.Println(a == b)
fmt.Println(*a == *b)
fmt.Println(&a)
fmt.Println(&b)
}
The output becomes something I did not expect:
true
true
0xc0000ae018
0xc0000ae020
Why does it become true in the first case?
From the Go language specification:
Pointers to distinct zero-size variables may or may not be equal.
MyStruct is struct{}, which is a zero-size type. Hence, a == b may be true or false.

CGo: How to avoid code duplication in switch

func GetStationPropsByPage(page string) []NameIndex {
var ni []NameIndex
switch page {
case "station_sys_props":
for _, n := range C.gStationSysProps {
if n.name == nil {
break
}
ni = append(ni, NameIndex{
Name: C.GoString(n.name),
No: int(n.no),
Type: string(n._type),
Remark: C.GoString(n.remark),
})
}
case "station_app_props":
for _, n := range C.gStationAppProps {
if n.name == nil {
break
}
ni = append(ni, NameIndex{
Name: C.GoString(n.name),
No: int(n.no),
Type: string(n._type),
Remark: C.GoString(n.remark),
})
}
}
return ni
}
In the code above, how can I write the for-loop only once? The type of C.gStationSysProps is generated by the compiler, for example, [24]main._Ctype_struct___0, and is different than C.gStationAppProps
Another related question is that for this to work, the C side has this code in the header file:
extern NameIndex gStationSysProps[24];
extern NameIndex gStationAppProps[30];
Without having to interop with Go, the C code was:
extern NameIndex gStationSysProps[];
extern NameIndex gStationAppProps[];
If I keep it without the max index, the compiling will go smoothly, however, the Go side cannot get any items, i.e. the array/slice is empty. Is it true that we have to specify array size in C in order to use CGo?

Using go/ast for iota declarations

I've been working with go/ast to parse go source code and copy it into another file as part of a vendoring exercise. I've got most things handled - functions, types etc - but I'm struggling with const declarations which use iota. I'm iterating through the items in ast.File.Scope.Objects and copying over the source for objects with Scope.Outer == nil and their Decl == ast.ValueSpec, basically implying top level variables and constants.
In a block of type:
const (
a = iota
b
c
d
)
...each one of them registers as a separate object, which is fair enough. However, I'm struggling to assign them with values because the objects can also be out of order when I'm iterating through them. I'm able to see the value as ast.Object.Data for these, but it also seems off when it's set to 1 << iota and so on. Does anyone have any thoughts on how I can get a grouped const declaration with the correct iota values assigned?
Thank you!
I was working on this problem for the exhaustive analyzer, which, during its enum discovery phase, needs to find constant values.
Playground: https://play.golang.org/p/nZLmgE4rJZH
Consider the following ConstDecl. It comprises of 3 ConstSpec, and each ConstSpec has 2 names. (This example uses iota, but the approach below should work generally for any ConstDecl.)
package example
const (
A, B = iota, iota * 100 // 0, 0
_, D // 1, 100
E, F // 2, 200
)
With go/ast and go/types (or x/tools/go/packages), we can obtain a *ast.GenDecl representing the above ConstDecl and a *types.Info for the package.
var decl *ast.GenDecl
var info *types.Info
The following will be true of decl.
decl.Tok == token.CONST
To obtain the constant values, we can do:
func printValuesConst(decl *ast.GenDecl, info *types.Info) {
for _, s := range decl.Specs {
v := s.(*ast.ValueSpec) // safe because decl.Tok == token.CONST
for _, name := range v.Names {
c := info.ObjectOf(name).(*types.Const)
fmt.Println(name, c.Val().ExactString())
}
}
}
This will, as expected, print:
A 0
B 0
_ 1
D 100
E 2
F 200
Side note: var instead of const
Note that the code above works for const blocks; for a var block we will have to obtain the value using v.Values[i] (assuming a value exists at the index i).
Playground: https://play.golang.org/p/f4mYjXvsvHB
decl.Tok == token.VAR
func printValuesVar(decl *ast.GenDecl, info *types.Info) {
for _, s := range decl.Specs {
v := s.(*ast.ValueSpec) // safe because decl.Tok == token.VAR
for i, name := range v.Names {
if len(v.Values) <= i {
fmt.Println(name, "(no AST value)")
continue
}
tv := info.Types[v.Values[i]]
if tv.Value == nil {
fmt.Println(name, "(not constant value)")
continue
}
fmt.Println(name, tv.Value.ExactString())
}
}
}

How does golang implemented the convertion between []byte and string?

I am not able to get the answer by checking the generated assemblies:
{
a := []byte{'a'}
s1 := string(a)
a[0] = 'b'
fmt.Println(s1) // a
}
{
a := "a"
b := []byte(a)
b[0] = 'b'
fmt.Println(a) // a
}
Why the observed behavior is happening?
Is there a description of how go interprets these lines of code?
What does go compiler do for type conversion?
This isn't so much a compiler issue as it is a language specification issue. The compiler can and will do strange things sometimes--what matters here is that whatever machine code the compiler ends up spitting out, it follows the rules laid out in the language specification.
As mentioned in the comments, the language specification defines the conversion of byte slices to and from string types like this:
Converting a slice of bytes to a string type yields a string whose successive bytes are the elements of the slice.
Converting a value of a string type to a slice of bytes type yields a slice whose successive elements are the bytes of the string.
In order to understand the behavior of your examples, you have to also read the definition of string types, also in the specification:
Strings are immutable: once created, it is impossible to change the contents of a string.
Because []byte is mutable, behind the scenes go must make a copy of the relevant data when converting to and from a string. This can be verified by printing the addresses of the 0th element of the []byte object and the pointer to the first element of data in the string object. Here is an example (and a Go Playground version):
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "a"
b := []byte(a)
ah := (*reflect.StringHeader)(unsafe.Pointer(&a))
fmt.Printf("a: %4s # %#x\n", a, ah.Data)
fmt.Printf("b: %v # %p\n\n", b, b)
c := []byte{'a'}
d := string(c)
dh := (*reflect.StringHeader)(unsafe.Pointer(&d))
fmt.Printf("c: %v # %p\n", c, c)
fmt.Printf("d: %4s # %#x\n", d, dh.Data)
}
The output looks like this:
a: a # 0x4c1ab2
b: [97] # 0xc00002c008
c: [97] # 0xc00002c060
d: a # 0x554e21
Notice that the pointer locations of the string and []byte are not the same and do not overlap. Therefore there is no expectation that changes to the []byte values will effect the string values in any way.
Okay, technically the result didn't have to be this way because I didn't make any changes in my example to the values of b or c. Technically the compiler could have taken a shortcut and simply called b a length=1 []byte starting at the same memory address as a. But that optimization would not be allowed if I did something like this instead:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := "a"
b := []byte(a)
b[0] = 'b'
ah := (*reflect.StringHeader)(unsafe.Pointer(&a))
fmt.Printf("a: %4s # %#x\n", a, ah.Data)
fmt.Printf("b: %v # %p\n\n", b, b)
}
Output:
a: a # 0x4c1ab2
b: [98] # 0xc00002c008
See this in action at the Go Playground.

What is the correct way to return from function?

I am confused how should I return a value from a function. I was trying the below example:
Passing false to function a
Value of c will be false in function a
It will make a recursive call and this will change the value of c from false to true
So function should return a string "true" and bool true but it is returning a string "false" and bool false
package main
import (
"fmt"
)
func a(c bool) (string, bool) {
if c {
return "true", true
}
a(true)
return "false", false
}
func main() {
s, ok := a(false)
fmt.Println(s, ok)
}
https://play.golang.org/p/uSIN2k0G3QW
Your recursive code will always return "false" false when c is false. Please see the correct code below.
package main
import (
"fmt"
)
func a(c bool) (string, bool) {
if c {
return "true", true
}
return a(true)
}
func main() {
s, ok := a(false)
fmt.Println(s, ok)
}
It's unclear what you think your code should do, but here's what it's actually doing:
You call a with the value false:
It checks if c is true:
if c {
but it's not, so it contiues
It calls a again, this time with the value true, but completely ignoring the result:
a(true)
Then it returns false:
return "false", false
This means that step #2 is a complete no-op (and is probably removed entirely by the compiler).
Maybe you want to do something with the returned value of a(true)?
This will result in a 2 level recursion. In first level you have c as false. Then you call a(true) and you are in second level which returns true. Now you are back in level 1 at line a(true) and the next line is to return false as string and bool. Hence you get false as final output.
I think you are trying to use the return value which is not the case right now.

Resources