Repository: go
Issue: cmd/compile: can't override variable with ldflags when initial value is a func
Labels: NeedsInvestigation,compiler/runtime

_(This might be better categorized under the linker, please edit as appropriate)_

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.21.4 darwin/arm64
</pre>

### Does this issue reproduce with the latest release?

Go 1.21.4 is the [latest version](https://go.dev/doc/devel/release#go1.21.0) as of this posting.

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/sethvargo/Library/Caches/go-build'
GOENV='/Users/sethvargo/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/sethvargo/Development/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/sethvargo/Development/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.21.4/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.21.4/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.4'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/sethvargo/Development/project/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/cp/qb9vbbkx4w36f6dclng481br00gy5b/T/go-build715077767=/tmp/go-build -gno-record-gcc-switches -fno-common'
</pre></details>

### What did you do?

I tried to override a package variable (that has a default value) using `ldflags`, all using the same command:

```sh
go run \
  -a \
  -trimpath \
  -ldflags="-s -w -X main.Version=1.2.3. -extldflags=-static"
```

My expectation is that the `Version` value is set to "1.2.3" and therefore the program prints "1.2.3". 

**✅ Explicit string type**

```go
package main
import "fmt"
var Version string = "development"
func main() {
  fmt.Println(Version) // correctly prints "1.2.3"
}
```

**✅ Implicit string type**

```go
package main
import "fmt"
var Version = "development"
func main() {
  fmt.Println(Version) // correctly prints "1.2.3"
}
```

**✅ Explicit string type `func() string`**

```go
package main
import "fmt"
var Version string = func() string {
  return "development"
}()
func main() {
  fmt.Println(Version) // prints "1.2.3"
}
```

**✅ Implicit string type `func() string`**

```go
package main
import "fmt"
var Version = func() string {
  return "development"
}()
func main() {
  fmt.Println(Version) // prints "1.2.3"
}
```

[Run with `-x` output](https://gist.githubusercontent.com/sethvargo/d375e763a65c415ed5cefb6bb76b6ade/raw/b1c474fd89949ac2cf90832da8cd7115829af625/success.txt)

**❌ Explicit string type complex `func() string`**

```go
package main
import "fmt"
var Version string = func() string {
  if true {
    return "development"
  }
  return "production"
}()
func main() {
  fmt.Println(Version) // ❌ always prints "development"
}
```

**❌ String concatenation with runtime variable**

```go
package main
import "fmt"
var Version string = "devel" + os.Getenv("FOO")
func main() {
  fmt.Println(Version) // ❌ always prints "devel"
}
```

[Run with `-x` output](https://gist.githubusercontent.com/sethvargo/d375e763a65c415ed5cefb6bb76b6ade/raw/b1c474fd89949ac2cf90832da8cd7115829af625/failure.txt)

At first I thought this was because the compiler was optimizing away the function call, but surely the compiler should also optimize out the "always-true" branch.


### What did you expect to see?

I expect the `ldflags` to override the variable.


### What did you see instead?

`ldflags` conditionally overrides the variable, depending on other variables I don't fully understand.


### What problem am I trying to solve?

I'd like to inject build information into the compiled binary, with sane fallback values if none are provided. Some of these values will be injected by the build process into the final binary, but if someone builds the binary themselves (or if they `go install` it), then I'd like _some_ reasonable build information. Fortunately modern versions of Go expose this, but it seems to be incompatible with allowing the values to be overridden:

```go
var Version string = func() string {
  if info, ok := debug.ReadBuildInfo(); ok {
    if v := info.Main.Version; v != "" {
      return v // e.g. "v0.0.1-alpha1.0.20231115..."
    }
  }

  return "source"
}()
```

I would _like_ the default version to come from the `debug` package, but still provide a mechanism for builders to inject/override with their own values. With the function definition above, it's impossible for a builder to override `Version`. Since some of these functions could be called hundreds or thousands of times, I don't want to make this a pure function call. The best I could come up with was to move the package into `internal` and leverage a combination of private variables and `once` functions, but it's not pleasing:

```go
package buildinfo

var version string
var Version string = sync.OnceValue(func() string {
  if v := version; v != "" {
    return v
  }

  if info, ok := debug.ReadBuildInfo(); ok {
    if v := info.Main.Version; v != "" {
      return v // e.g. "v0.0.1-alpha1.0.20231115..."
    }
  }

  return "source"
})()
```