design/4238-go12nil: copy from go.dev/s/go12nil

A step toward gathering design docs in one place.

Change-Id: Ic4c0d2b27d4e91a30272b260c891cffe42c371e6
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/385794
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
diff --git a/design/4238-go12nil.md b/design/4238-go12nil.md
new file mode 100644
index 0000000..a19f66e
--- /dev/null
+++ b/design/4238-go12nil.md
@@ -0,0 +1,192 @@
+# Go 1.2 Field Selectors and Nil Checks
+
+Author: Russ Cox
+
+Last updated: July 2013
+
+Discussion at https://go.dev/issue/4238.
+
+Originally at https://go.dev/s/go12nil.
+
+Implemented in Go 1.2 release.
+
+## Abstract
+
+For Go 1.2, we need to define that, if `x` is a pointer to a struct
+type and `x == nil`, `&x.Field` causes a runtime panic rather than
+silently producing an unusable pointer.
+
+## Background
+
+
+Today, if you have:
+
+```Go
+package main
+
+type T struct {
+        Field1 int32
+        Field2 int32
+}
+
+type T2 struct {
+        X [1<<24]byte
+        Field int32
+}
+
+func main() {
+        var x *T
+        p1 := &x.Field1
+        p2 := &x.Field2
+        var x2 *T2
+        p3 := &x2.Field
+}
+```
+
+then:
+
+* `p1 == nil`; dereferencing it causes a panic
+* `p2 != nil` (it has pointer value 4); but dereferencing it still
+  causes a panic
+* p3 is not computed: `&x2.Field` panics to avoid producing a pointer
+  that might point into mapped memory.
+
+The spec does not define what should happen when `&x.Field` is evaluated
+for `x == nil`.
+The answer probably should not depend on `Field`’s offset within the
+struct.
+The current behavior is at best merely historical accident; it was
+definitely not thought through or discussed.
+
+Those three behaviors are three possible definitions.
+The behavior for `p2` is clearly undesirable, since it creates
+unusable pointers that cannot be detected as unusable.
+hat leaves `p1` (`&x.Field` is `nil` if `x` is `nil`) and `p3`
+(`&x.Field` panics if `x` is `nil`).
+
+An analogous form of the question concerns `&x[i]` where `x` is a
+`nil` pointer to an array.
+he current behaviors match those of the struct exactly, depending in
+the same way on both the offset of the field and the overall size of
+the array.
+
+A related question is how `&*x` should evaluate when `x` is `nil`.
+In C, `&*x == x` even when `x` is `nil`.
+The spec again is silent.
+The gc compilers go out of their way to implement the C rule (it
+seemed like a good idea at a time).
+
+A simplified version of a recent example is:
+
+```Go
+        type T struct {
+                f int64
+                sync.Mutex
+        }
+
+        var x *T
+
+        x.Lock()
+```
+
+The method call turns into `(&x.Mutex).Lock()`, which today is passed
+a receiver with pointer value `8` and panics inside the method,
+accessing a `sync.Mutex` field.
+
+
+## Proposed Definition
+
+If `x` is a `nil` pointer to a struct, then evaluating `&x.Field`
+always panics.
+
+If `x` is a `nil` pointer to an array, then evaluating `&x[i]` panics
+or `x[i:j]` panics.
+
+If `x` is a `nil` pointer, then evaluating `&*x` panics.
+
+In general, the result of an evaluation of `&expr` either panics or
+returns a non-nil pointer.
+
+## Rationale
+
+The alternative, defining `&x.Field == nil` when `x` is `nil`, delays
+the error check.
+That feels more like something that belongs in a dynamically typed
+language like Python or JavaScript than in Go.
+Put another way, it pushes the panic farther away from the problem.
+
+We have not seen a compelling use case for allowing `&x.Field == nil`.
+
+Panicking during `&x.Field` is no more expensive (perhaps less) than
+defining `&x.Field == nil`.
+
+It is difficult to justify allowing `&*x` but not `&x.Field`.
+They are different expressions of the same computation.
+
+The guarantee that `&expr`—when it evaluates successfully—is always a
+non-nil pointer makes intuitive sense and avoids a surprise: how can
+you take the address of something and get `nil`?
+
+## Implementation
+
+The addressable expressions are: “a variable, pointer indirection, or
+slice indexing operation; or a field selector of an addressable struct
+operand; or an array indexing operation of an addressable array.”
+
+The address of a variable can never be `nil`; the address of a slice
+indexing operation is already checked because a `nil` slice will have
+`0` length, so any index is invalid.
+
+That leaves pointer indirections, field selector of struct, and index
+of array, confirming at least that we’re considering the complete set
+of cases.
+
+Assuming `x` is in register AX, the current x86 implementation of case
+`p3` is to read from the memory `x` points at:
+
+```
+	TEST 0(AX), AX
+```
+
+That causes a fault when `x` is nil.
+Unfortunately, it also causes a read from the memory location `x`,
+even if the actual field being addressed is later in memory.
+This can cause unnecessary cache conflicts if different goroutines own
+different sections of a large array and one is writing to the first
+entry.
+
+(It is tempting to use a conditional move instruction:
+
+```
+	TEST AX, AX
+	CMOVZ 0, AX
+```
+
+Unfortunately, the definition of the conditional move is that the load
+is unconditional and only the assignment is conditional, so the fault
+at address `0` would happen always.)
+
+An alternate implementation would be to test `x` itself and use a
+conditional jump:
+
+```
+	TEST AX, AX
+	JNZ ok  (branch hint: likely)
+	MOV $0, 0
+ok:
+```
+
+This is more code (something like 7 bytes instead of 3) but may run
+more efficiently, as it avoids spurious memory references and will be
+predicted easily.
+
+(Note that defining `&x.Field == nil` would require at least that much
+code, if not a little more, except when the offset is `0`.)
+
+It will probably be important to have a basic flow analysis for
+variables, so that the compiler can avoid re-testing the same pointer
+over and over in a given function.
+I started on that general topic a year ago and got a prototype working
+but then put it aside (the goal then was index bounds check
+elimination).
+It could be adapted easily for nil check elimination.