Normalization of method selectors

Go allows simplified forms of some selectors.

For example, in the following program, t1.M1 is a simplified form of (*t1).M1, and t2.M2 is a simplified form of (&t2).M2. At compile time, the compiler will normalize the simplified forms to their original respective full forms.

The following program prints 0 and 9, because the modification to t1.X has no effects on the evaluation result of *t1 during evaluating (*t1).M1.

package main

type T struct {
	X int
}

func (t T) M1() int {
	return t.X
}

func (t *T) M2() int {
	return t.X
}

func main() {
	var t1 = new(T)
	var f1 = t1.M1 // <=> (*t1).M1
	t1.X = 9
	println(f1()) // 0 
	
	var t2 T
	var f2 = t2.M2 // <=> (&t2).M2
	t2.X = 9
	println(f2()) // 9
}

In the following code, the function foo runs okay, but the function bar will produce a panic. The reason is s.M is a simplified form of (*s.T).M. At compile time, the compiler will normalize the simplified form to it original full form. At runtime, if s.T is nil, then the evaluation of *s.T will cause a panic. The two modifications to s.T have no effects on the evaluation result of *s.T.

package main

type T struct {
	X int
}

func (t T) M() int {
	return t.X
}

type S struct {
	*T
}

func foo() {
	var s = S{T: new(T)}
	var f = s.M // <=> (*s.T).M
	s.T = nil
	f()
}

func bar() {
	var s S
	var f = s.M // panic
	s.T = new(T)
	f()
}

func main() {
	foo()
	bar()
}

Please note that, interface method values and method values got through reflection will be expanded to the promoted method values with a delay. For example, in the following program, the modification to s.T.X has effects on the method values got through reflection and interface ways.

package main

import "reflect"

type T struct {
	X int
}

func (t T) M() int {
	return t.X
}

type S struct {
	*T
}

func main() {
	var s = S{T: new(T)}
	var f = s.M // <=> (*s.T).M
	var g = reflect.ValueOf(&s).Elem().
		MethodByName("M").
		Interface().(func() int)
	var h = interface{M() int}(s).M
	s.T.X = 3
	println( f() ) // 0
	println( g() ) // 3
	println( h() ) // 3
}

Source: https://github.com/golang/go/issues/47863