dwtest: add test for go#72053

Adds a test for https://go.dev/issue/72053
Note the fix is submitted as https://go.dev/cl/656736

For [golang/go#72053](https://github.com/golang/go/issues/72053)

Change-Id: Iea247439f95cce85bf9a1560dd475be5048ec97a
GitHub-Last-Rev: dcd050f0539dc6c11d75ae7a9da197aa99fe6c01
GitHub-Pull-Request: golang/debug#22
Reviewed-on: https://go-review.googlesource.com/c/debug/+/657355
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/dwtest/dwloc_test.go b/dwtest/dwloc_test.go
index 1c046cb..16ae923 100644
--- a/dwtest/dwloc_test.go
+++ b/dwtest/dwloc_test.go
@@ -118,13 +118,15 @@
 
 // gobuild is a helper to build a Go program from source code,
 // so that we can inspect selected bits of DWARF in the resulting binary.
-// Return value is binary path.
-func gobuild(t *testing.T, sourceCode string, pname string, dir string) string {
+// The first return value is the path to the binary compiled with optimizations,
+// the second is the path to the binary compiled without optimizations.
+func gobuild(t *testing.T, sourceCode string, pname string, dir string) (string, string) {
 	spath := filepath.Join(dir, pname+".go")
 	if err := os.WriteFile(spath, []byte(sourceCode), 0644); err != nil {
 		t.Fatalf("write to %s failed: %s", spath, err)
 	}
 	epath := filepath.Join(dir, pname+".exe")
+	nooppath := filepath.Join(dir, pname+".noop.exe")
 
 	// A note on this build: Delve currently has problems digesting
 	// PIE binaries on Windows; until this can be straightened out,
@@ -134,13 +136,22 @@
 		t.Logf("%% build output: %s\n", b)
 		t.Fatalf("build failed: %s", err)
 	}
-	return epath
+	cmd = exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-trimpath", "-buildmode=exe", "-o", nooppath, spath)
+	if b, err := cmd.CombinedOutput(); err != nil {
+		t.Logf("%% build output: %s\n", b)
+		t.Fatalf("build failed: %s", err)
+	}
+	return epath, nooppath
 }
 
 const programSourceCode = `
 package main
 
-import "context"
+import (
+	"context"
+	"strings"
+	"fmt"
+)
 
 var G int
 
@@ -178,10 +189,29 @@
 	return nil, nil
 }
 
+//go:noinline
+func Issue72053() {
+		u := Address{Addr: "127.0.0.1"}
+		fmt.Println(u)
+}
+
+type Address struct {
+		TLS  bool
+		Addr string
+}
+
+//go:noinline
+func (a Address) String() string {
+		sb := new(strings.Builder)
+		sb.WriteString(a.Addr)
+		return sb.String()
+}
+
 func main() {
 	Issue47354("poo")
 	var d DB
 	d.Issue46845(context.Background(), nil, func(error) {}, "foo", nil)
+	Issue72053()
 }
 
 `
@@ -236,6 +266,18 @@
 	}
 }
 
+func testIssue72053(t *testing.T, harnessPath string, ppath string) {
+	testenv.NeedsGo1Point(t, 25)
+	testenv.NeedsArch(t, "amd64")
+
+	want := "1: in-param \"a\" loc=\"{ [0: S=1 RAX] [1: S=7 addr=0x0] [2: S=8 RBX] [3: S=8 RCX] }\"\n2: out-param \"~r0\" loc=\"addr=fa8\""
+	got := runHarness(t, harnessPath, ppath, "main.Address.String")
+	if got != want {
+		t.Errorf("failed Issue72053 arch %s:\ngot: %q\nwant: %q",
+			runtime.GOARCH, got, want)
+	}
+}
+
 // testRuntimeThrow verifies that we have well-formed DWARF for the
 // single input parameter of 'runtime.throw'. This function is
 // particularly important to handle correctly, since it is
@@ -293,7 +335,7 @@
 	// Build program to inspect. NB: we're building at default (with
 	// optimization); it might also be worth doing a "-l -N" build
 	// to verify the location expressions in that case.
-	ppath := gobuild(t, programSourceCode, "prog", tdir)
+	ppath, nooppath := gobuild(t, programSourceCode, "prog", tdir)
 
 	// Sub-tests for each function we want to inspect.
 	t.Run("Issue47354", func(t *testing.T) {
@@ -304,6 +346,10 @@
 		t.Parallel()
 		testIssue46845(t, harnessPath, ppath)
 	})
+	t.Run("Issue72053", func(t *testing.T) {
+		t.Parallel()
+		testIssue72053(t, harnessPath, nooppath)
+	})
 	t.Run("RuntimeThrow", func(t *testing.T) {
 		t.Parallel()
 		testRuntimeThrow(t, harnessPath, nooptHarnessPath, ppath)
diff --git a/internal/testenv/testenv.go b/internal/testenv/testenv.go
index 7ceff42..75cec76 100644
--- a/internal/testenv/testenv.go
+++ b/internal/testenv/testenv.go
@@ -131,3 +131,13 @@
 		t.Skipf("running Go version %q is version 1.%d, older than required 1.%d", runtime.Version(), Go1Point(), x)
 	}
 }
+
+// NeedsArch skips test if the current arch is different than the one required.
+func NeedsArch(t Testing, arch string) {
+	if t, ok := t.(helperer); ok {
+		t.Helper()
+	}
+	if runtime.GOARCH != arch {
+		t.Skipf("current arch is %q, test requires %q", runtime.GOARCH, arch)
+	}
+}