[dev.typeparams] cmd/compile: add test for number of instantiations

Add a test for a generic sort function, operating on several different
pointer types (across two packages), so they should all share the same
shape-based instantiation. Actually check that only one instantiation of
Sort is created using 'go tool nm', and also check that the output is
correct.

In order to do the test on the executable using 'go nm', added this as a
'go test' in cmd/compile/internal/test.

Added the genembed.go test that I meant to include with a previous CL.

Change-Id: I9962913c2f1809484c2b1dfef3b07e4c8770731c
Reviewed-on: https://go-review.googlesource.com/c/go/+/354696
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
diff --git a/src/cmd/compile/internal/test/inst_test.go b/src/cmd/compile/internal/test/inst_test.go
new file mode 100644
index 0000000..59a67cb
--- /dev/null
+++ b/src/cmd/compile/internal/test/inst_test.go
@@ -0,0 +1,71 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package test
+
+import (
+	"internal/goexperiment"
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"testing"
+)
+
+// TestInst tests that only one instantiation of Sort is created, even though generic
+// Sort is used for multiple pointer types across two packages.
+func TestInst(t *testing.T) {
+	if goexperiment.Unified {
+		t.Skip("unified currently does stenciling, not dictionaries")
+	}
+	testenv.MustHaveGoBuild(t)
+	testenv.MustHaveGoRun(t)
+
+	var tmpdir string
+	var err error
+	tmpdir, err = ioutil.TempDir("", "TestDict")
+	if err != nil {
+		t.Fatalf("Failed to create temporary directory: %v", err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	// Build ptrsort.go, which uses package mysort.
+	var output []byte
+	filename := "ptrsort.go"
+	exename := "ptrsort"
+	outname := "ptrsort.out"
+	gotool := testenv.GoToolPath(t)
+	dest := filepath.Join(tmpdir, exename)
+	cmd := exec.Command(gotool, "build", "-o", dest, filepath.Join("testdata", filename))
+	if output, err = cmd.CombinedOutput(); err != nil {
+		t.Fatalf("Failed: %v:\nOutput: %s\n", err, output)
+	}
+
+	// Test that there is exactly one shape-based instantiation of Sort in
+	// the executable.
+	cmd = exec.Command(gotool, "tool", "nm", dest)
+	if output, err = cmd.CombinedOutput(); err != nil {
+		t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
+	}
+	re := regexp.MustCompile(`\bSort\[.*shape.*\]`)
+	r := re.FindAllIndex(output, -1)
+	if len(r) != 1 {
+		t.Fatalf("Wanted 1 instantiations of Sort function, got %d\n", len(r))
+	}
+
+	// Actually run the test and make sure output is correct.
+	cmd = exec.Command(gotool, "run", filepath.Join("testdata", filename))
+	if output, err = cmd.CombinedOutput(); err != nil {
+		t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
+	}
+	out, err := ioutil.ReadFile(filepath.Join("testdata", outname))
+	if err != nil {
+		t.Fatalf("Could not find %s\n", outname)
+	}
+	if string(out) != string(output) {
+		t.Fatalf("Wanted output %v, got %v\n", string(out), string(output))
+	}
+}
diff --git a/src/cmd/compile/internal/test/testdata/mysort/mysort.go b/src/cmd/compile/internal/test/testdata/mysort/mysort.go
new file mode 100644
index 0000000..14852c8
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/mysort/mysort.go
@@ -0,0 +1,40 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Generic sort function, tested with two different pointer types.
+
+package mysort
+
+import (
+	"fmt"
+)
+
+type LessConstraint[T any] interface {
+	Less(T) bool
+}
+
+//go:noinline
+func Sort[T LessConstraint[T]](x []T) {
+	n := len(x)
+	for i := 1; i < n; i++ {
+		for j := i; j > 0 && x[j].Less(x[j-1]); j-- {
+			x[j], x[j-1] = x[j-1], x[j]
+		}
+	}
+}
+
+type MyInt struct {
+	Value int
+}
+
+func (a *MyInt) Less(b *MyInt) bool {
+	return a.Value < b.Value
+}
+
+//go:noinline
+func F() {
+	sl1 := []*MyInt{&MyInt{4}, &MyInt{3}, &MyInt{8}, &MyInt{7}}
+	Sort(sl1)
+	fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3])
+}
diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.go b/src/cmd/compile/internal/test/testdata/ptrsort.go
new file mode 100644
index 0000000..6cc7ba4
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/ptrsort.go
@@ -0,0 +1,30 @@
+package main
+
+// Test generic sort function with two different pointer types in different packages,
+// make sure only one instantiation is created.
+
+import (
+	"fmt"
+
+	"./mysort"
+)
+
+type MyString struct {
+	string
+}
+
+func (a *MyString) Less(b *MyString) bool {
+	return a.string < b.string
+}
+
+func main() {
+	mysort.F()
+
+	sl1 := []*mysort.MyInt{{7}, {1}, {4}, {6}}
+	mysort.Sort(sl1)
+	fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3])
+
+	sl2 := []*MyString{{"when"}, {"in"}, {"the"}, {"course"}, {"of"}}
+	mysort.Sort(sl2)
+	fmt.Printf("%v %v %v %v %v\n", sl2[0], sl2[1], sl2[2], sl2[3], sl2[4])
+}
diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.out b/src/cmd/compile/internal/test/testdata/ptrsort.out
new file mode 100644
index 0000000..41f1621
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/ptrsort.out
@@ -0,0 +1,3 @@
+&{3} &{4} &{7} &{8}
+&{1} &{4} &{6} &{7}
+&{course} &{in} &{of} &{the} &{when}
diff --git a/test/typeparam/genembed.go b/test/typeparam/genembed.go
new file mode 100644
index 0000000..43ab3d6
--- /dev/null
+++ b/test/typeparam/genembed.go
@@ -0,0 +1,52 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test wrappers/interfaces for generic type embedding another generic type.
+
+package main
+
+import "fmt"
+
+type A[T any] struct {
+	B[T]
+}
+
+type B[T any] struct {
+	val T
+}
+
+func (b *B[T]) get() T {
+	return b.val
+}
+
+type getter[T any] interface {
+	get() T
+}
+
+//go:noinline
+func doGet[T any](i getter[T]) T {
+	return i.get()
+}
+
+//go:noline
+func doGet2[T any](i interface{}) T {
+	i2 := i.(getter[T])
+	return i2.get()
+}
+
+func main() {
+	a := A[int]{B: B[int]{3}}
+	var i getter[int] = &a
+
+	if got, want := doGet(i), 3; got != want {
+		panic(fmt.Sprintf("got %v, want %v", got, want))
+	}
+
+	as := A[string]{B: B[string]{"abc"}}
+	if got, want := doGet2[string](&as), "abc"; got != want {
+		panic(fmt.Sprintf("got %v, want %v", got, want))
+	}
+}