diff --git a/test/go/nil_deref_address_test.go b/test/go/nil_deref_address_test.go new file mode 100644 index 0000000000..e2a9506b3d --- /dev/null +++ b/test/go/nil_deref_address_test.go @@ -0,0 +1,130 @@ +package gotest + +import ( + "fmt" + "strings" + "testing" +) + +type nilDerefAddressStruct struct { + i int + x [2]int +} + +var nilDerefAddressSink any + +func TestNilDerefAddressOperationsPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "address of dereference", + f: func() { + var p *int + nilDerefAddressSink = &*p + }, + }, + { + name: "field address", + f: func() { + var p *nilDerefAddressStruct + nilDerefAddressSink = &p.i + }, + }, + { + name: "array pointer element address", + f: func() { + var p *[2]int + nilDerefAddressSink = &p[0] + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func TestNilDerefPrintedCompositeLoadsPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "slice pointer load", + f: func() { + var p *[]byte + println(*p) + }, + }, + { + name: "string pointer load", + f: func() { + var p *string + println(*p) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func TestNilDerefPrintedFieldLoadPanic(t *testing.T) { + expectNilDerefAddressPanic(t, func() { + var p *nilDerefAddressStruct + println(p.i) + }) +} + +func TestNilDerefInterfaceCopiesPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "array pointer to interface", + f: func() { + var p *[4]int + nilDerefAddressSink = *p + }, + }, + { + name: "struct pointer to interface", + f: func() { + var p *nilDerefAddressStruct + nilDerefAddressSink = *p + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func expectNilDerefAddressPanic(t *testing.T, f func()) { + t.Helper() + defer func() { + err := recover() + if err == nil { + t.Fatal("expected nil pointer dereference panic") + } + if got := nilDerefAddressPanicString(err); !strings.Contains(got, "nil pointer dereference") { + t.Fatalf("panic = %q, want nil pointer dereference", got) + } + }() + f() +} + +func nilDerefAddressPanicString(v any) string { + if err, ok := v.(interface{ Error() string }); ok { + return err.Error() + } + return fmt.Sprint(v) +} diff --git a/test/go/nil_panic_runtime_error_test.go b/test/go/nil_panic_runtime_error_test.go new file mode 100644 index 0000000000..a65b12f9cc --- /dev/null +++ b/test/go/nil_panic_runtime_error_test.go @@ -0,0 +1,67 @@ +package gotest + +import ( + "runtime" + "strings" + "testing" +) + +type nilPanicRuntimeInterface interface { + M() +} + +func TestNilPanicValuesAreRuntimeErrors(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "generic nil interface method", + f: func() { + nilPanicRuntimeCall[nilPanicRuntimeInterface](nil) + }, + }, + { + name: "nil interface method expression", + f: func() { + nilPanicRuntimeMethodExpression[int]() + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := expectRuntimeErrorPanic(t, tt.f) + if !strings.Contains(err.Error(), "nil pointer dereference") { + t.Fatalf("panic = %q, want nil pointer dereference", err.Error()) + } + }) + } +} + +func nilPanicRuntimeCall[P nilPanicRuntimeInterface](p P) { + p.M() +} + +func nilPanicRuntimeMethodExpression[T any]() { + interface{ M() T }.M(nil) +} + +func expectRuntimeErrorPanic(t *testing.T, f func()) runtime.Error { + t.Helper() + var recovered any + func() { + defer func() { + recovered = recover() + }() + f() + }() + if recovered == nil { + t.Fatal("expected panic") + } + err, ok := recovered.(runtime.Error) + if !ok { + t.Fatalf("panic type = %T, want runtime.Error", recovered) + } + return err +} diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index e1b20e973a..34231d6fb4 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -1747,10 +1747,6 @@ xfails: directive: run case: method5.go reason: current main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: nilptr2.go - reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: noinit.go @@ -1818,11 +1814,6 @@ xfails: directive: run case: method5.go reason: go1.24 goroot run failure on linux/amd64 - - version: go1.24 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.24 goroot run failure on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -1898,11 +1889,6 @@ xfails: directive: run case: method5.go reason: go1.25 goroot run failure on linux/amd64 - - version: go1.25 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.25 goroot run failure on linux/amd64 - version: go1.25 platform: linux/amd64 directive: run @@ -1983,11 +1969,6 @@ xfails: directive: run case: method5.go reason: go1.26 goroot run failure on linux/amd64 - - version: go1.26 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.26 goroot run failure on linux/amd64 - version: go1.26 platform: linux/amd64 directive: run @@ -3005,15 +2986,15 @@ xfails: reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run - case: fixedbugs/issue38496.go + case: fixedbugs/issue43835.go reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run - case: fixedbugs/issue43835.go + case: fixedbugs/issue47928.go reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run - case: fixedbugs/issue47928.go + case: fixedbugs/issue38496.go reason: current main goroot run failure on darwin/arm64 - version: go1.26 platform: darwin/arm64 @@ -3041,10 +3022,6 @@ xfails: directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: typeparam/issue51521.go - reason: nil-deref panic value does not implement error on darwin/arm64 - platform: darwin/arm64 directive: run case: typeparam/mdempsky/15.go @@ -3065,10 +3042,6 @@ xfails: directive: run case: fixedbugs/issue26094.go reason: current main goroot run failure on linux/amd64 - - platform: linux/amd64 - directive: run - case: fixedbugs/issue38496.go - reason: current main goroot run failure on linux/amd64 - platform: linux/amd64 directive: run case: fixedbugs/issue43835.go @@ -3095,6 +3068,10 @@ xfails: directive: run case: fixedbugs/issue8132.go reason: current main goroot run failure on linux/amd64 + - platform: linux/amd64 + directive: run + case: fixedbugs/issue38496.go + reason: current main goroot run failure on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -3117,10 +3094,6 @@ xfails: directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on linux/amd64 - - platform: linux/amd64 - directive: run - case: typeparam/issue51521.go - reason: nil-deref panic value does not implement error on linux/amd64 - platform: linux/amd64 directive: run case: typeparam/mdempsky/15.go