mirror of
https://github.com/golang/go
synced 2024-11-23 00:10:07 -07:00
spec: clarify language on package-level variable initialization
The very first paragraph on "Package initialization" stated that "variables are initialized in declaration order, but after any variables they might depend on". This phrasing was easily misread as "declaration order is the first sorting criteria" and then contradicted what the subsequent paragraphs spelled out in precise detail. Instead, variable initialization proceeds by repeatedly determining a set of ready to initialize variables, and then selecting from that set the variable declared earliest. That is, declaration order is the second sorting criteria. Also, for the purpose of variable initialization, declarations introducing blank (_) variables are considered like any other variables (their initialization expressions may have side-effects and affect initialization order), even though blank identifiers are not "declared". This CL adds clarifying language regarding these two issues and the supporting example. Both gccgo and go/types implement this behavior. cmd/compile has a long-standing issue (#22326). The spec also did not state in which order multiple variables initialized by a single (multi-value) initialization expression are handled. This CL adds a clarifying paragraph: If any such variable is initialized, all that declaration's variables are initialized at the same time. This behavior matches user expectation: We are not expecting to observe partially initialized sets of variables in declarations such as "var a, b, c = f()". It also matches existing cmd/compile and go/types (but not gccgo) behavior. Finally, cmd/compile, gccgo, and go/types produce different initialization orders in (esoteric) cases where hidden (not detected with existing rules) dependencies exist. Added a sentence and example clarifying how much leeway compilers have in those situations. The goal is to preserve the ability to use static initialization while at the same time maintain the relative initialization order of variables with detected dependencies. Fixes #31292. Updates #22326. Change-Id: I0a369abff8cfce27afc975998db875f5c580caa2 Reviewed-on: https://go-review.googlesource.com/c/go/+/175980 Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
5d98330354
commit
451cf3e2cd
@ -1,6 +1,6 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of March 13, 2019",
|
||||
"Subtitle": "Version of May 13, 2019",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
@ -6362,16 +6362,16 @@ var t T
|
||||
<h3 id="Package_initialization">Package initialization</h3>
|
||||
|
||||
<p>
|
||||
Within a package, package-level variables are initialized in
|
||||
<i>declaration order</i> but after any of the variables
|
||||
they <i>depend</i> on.
|
||||
Within a package, package-level variable initialization proceeds stepwise,
|
||||
with each step selecting the variable earliest in <i>declaration order</i>
|
||||
which has no dependencies on uninitialized variables.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
More precisely, a package-level variable is considered <i>ready for
|
||||
initialization</i> if it is not yet initialized and either has
|
||||
no <a href="#Variable_declarations">initialization expression</a> or
|
||||
its initialization expression has no dependencies on uninitialized variables.
|
||||
its initialization expression has no <i>dependencies</i> on uninitialized variables.
|
||||
Initialization proceeds by repeatedly initializing the next package-level
|
||||
variable that is earliest in declaration order and ready for initialization,
|
||||
until there are no variables ready for initialization.
|
||||
@ -6383,6 +6383,23 @@ process ends, those variables are part of one or more initialization cycles,
|
||||
and the program is not valid.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Multiple variables on the left-hand side of a variable declaration initialized
|
||||
by single (multi-valued) expression on the right-hand side are initialized
|
||||
together: If any of the variables on the left-hand side is initialized, all
|
||||
those variables are initialized in the same step.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var x = a
|
||||
var a, b = f() // a and b are initialized together, before x is initialized
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
For the purpose of package initialization, <a href="#Blank_identifier">blank</a>
|
||||
variables are treated like any other variables in declarations.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The declaration order of variables declared in multiple files is determined
|
||||
by the order in which the files are presented to the compiler: Variables
|
||||
@ -6424,22 +6441,16 @@ or to a function or method that depends on <code>y</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Dependency analysis is performed per package; only references referring
|
||||
to variables, functions, and methods declared in the current package
|
||||
are considered.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, given the declarations
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var (
|
||||
a = c + b
|
||||
b = f()
|
||||
c = f()
|
||||
d = 3
|
||||
a = c + b // == 9
|
||||
b = f() // == 4
|
||||
c = f() // == 5
|
||||
d = 3 // == 5 after initialization has finished
|
||||
)
|
||||
|
||||
func f() int {
|
||||
@ -6450,6 +6461,39 @@ func f() int {
|
||||
|
||||
<p>
|
||||
the initialization order is <code>d</code>, <code>b</code>, <code>c</code>, <code>a</code>.
|
||||
Note that the order of subexpressions in initialization expressions is irrelevant:
|
||||
<code>a = c + b</code> and <code>a = b + c</code> result in the same initialization
|
||||
order in this example.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dependency analysis is performed per package; only references referring
|
||||
to variables, functions, and (non-interface) methods declared in the current
|
||||
package are considered. If other, hidden, data dependencies exists between
|
||||
variables, the initialization order between those variables is unspecified.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For instance, given the declarations
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var x = I(T{}).ab() // x has an undetected, hidden dependency on a and b
|
||||
var _ = sideEffect() // unrelated to x, a, or b
|
||||
var a = b
|
||||
var b = 42
|
||||
|
||||
type I interface { ab() []int }
|
||||
type T struct{}
|
||||
func (T) ab() []int { return []int{a, b} }
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
the variable <code>a</code> will be initialized after <code>b</code> but
|
||||
whether <code>x</code> is initialized before <code>b</code>, between
|
||||
<code>b</code> and <code>a</code>, or after <code>a</code>, and
|
||||
thus also the moment at which <code>sideEffect()</code> is called (before
|
||||
or after <code>x</code> is initialized) is not specified.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
Loading…
Reference in New Issue
Block a user