[release-branch.go1.21] all: merge master (b7e7467) into release-branch.go1.21

Merge List:

+ 2023-06-15 b7e7467865 test/codegen: add fsqrt test for riscv64
+ 2023-06-15 befec5ddbb text/template: set variables correctly in range assignment
+ 2023-06-15 c5463218a2 cmd/api: skip TestIssue29837 when -short is set
+ 2023-06-15 9fc84363d1 cmd/asm: fix encoding errors for FMOVD and FMOVS instructions on arm64
+ 2023-06-14 da94586aa3 cmd/go: check for errors reading gccgo package list
+ 2023-06-14 b01cd41b46 cmd/go: use gover.Local for $goversion in TestScript
+ 2023-06-14 3aea422e2c crypto/x509: use synthetic root for platform testing

Change-Id: Icec55130749c52eef75c0e0325d889ff18b067f6
diff --git a/src/cmd/api/api_test.go b/src/cmd/api/api_test.go
index 53ae6fd..a5ac49c 100644
--- a/src/cmd/api/api_test.go
+++ b/src/cmd/api/api_test.go
@@ -209,16 +209,7 @@
 	}
 }
 
-func TestIssue21181(t *testing.T) {
-	if testing.Short() {
-		t.Skip("skipping with -short")
-	}
-	if *flagCheck {
-		// slow, not worth repeating in -check
-		t.Skip("skipping with -check set")
-	}
-	testenv.MustHaveGoBuild(t)
-
+var warmupCache = sync.OnceFunc(func() {
 	// Warm up the import cache in parallel.
 	var wg sync.WaitGroup
 	for _, context := range contexts {
@@ -230,6 +221,19 @@
 		}()
 	}
 	wg.Wait()
+})
+
+func TestIssue21181(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping with -short")
+	}
+	if *flagCheck {
+		// slow, not worth repeating in -check
+		t.Skip("skipping with -check set")
+	}
+	testenv.MustHaveGoBuild(t)
+
+	warmupCache()
 
 	for _, context := range contexts {
 		w := NewWalker(context, "testdata/src/issue21181")
@@ -243,11 +247,17 @@
 }
 
 func TestIssue29837(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping with -short")
+	}
 	if *flagCheck {
 		// slow, not worth repeating in -check
 		t.Skip("skipping with -check set")
 	}
 	testenv.MustHaveGoBuild(t)
+
+	warmupCache()
+
 	for _, context := range contexts {
 		w := NewWalker(context, "testdata/src/issue29837")
 		_, err := w.ImportFrom("p", "", 0)
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index 534a0b3..11bd678 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -238,6 +238,8 @@
 	FMOVS	$0, F0                          // e003271e
 	FMOVD	ZR, F0                          // e003679e
 	FMOVS	ZR, F0                          // e003271e
+	FMOVD	F1, ZR                          // 3f00669e
+	FMOVS	F1, ZR                          // 3f00261e
 	VUADDW	V9.B8, V12.H8, V14.H8           // 8e11292e
 	VUADDW	V13.H4, V10.S4, V11.S4          // 4b116d2e
 	VUADDW	V21.S2, V24.D2, V29.D2          // 1d13b52e
diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go
index 0fc85da..d4d0a71 100644
--- a/src/cmd/go/internal/work/action.go
+++ b/src/cmd/go/internal/work/action.go
@@ -382,16 +382,23 @@
 func readpkglist(shlibpath string) (pkgs []*load.Package) {
 	var stk load.ImportStack
 	if cfg.BuildToolchainName == "gccgo" {
-		f, _ := elf.Open(shlibpath)
+		f, err := elf.Open(shlibpath)
+		if err != nil {
+			base.Fatal(fmt.Errorf("failed to open shared library: %v", err))
+		}
 		sect := f.Section(".go_export")
-		data, _ := sect.Data()
-		scanner := bufio.NewScanner(bytes.NewBuffer(data))
-		for scanner.Scan() {
-			t := scanner.Text()
-			var found bool
-			if t, found = strings.CutPrefix(t, "pkgpath "); found {
-				t = strings.TrimSuffix(t, ";")
-				pkgs = append(pkgs, load.LoadPackageWithFlags(t, base.Cwd(), &stk, nil, 0))
+		if sect == nil {
+			base.Fatal(fmt.Errorf("%s: missing .go_export section", shlibpath))
+		}
+		data, err := sect.Data()
+		if err != nil {
+			base.Fatal(fmt.Errorf("%s: failed to read .go_export section: %v", shlibpath, err))
+		}
+		pkgpath := []byte("pkgpath ")
+		for _, line := range bytes.Split(data, []byte{'\n'}) {
+			if path, found := bytes.CutPrefix(line, pkgpath); found {
+				path = bytes.TrimSuffix(path, []byte{';'})
+				pkgs = append(pkgs, load.LoadPackageWithFlags(string(path), base.Cwd(), &stk, nil, 0))
 			}
 		}
 	} else {
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index d009988..624c5bf 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -14,20 +14,18 @@
 	"bytes"
 	"context"
 	"flag"
-	"fmt"
-	"go/build"
 	"internal/testenv"
 	"internal/txtar"
 	"net/url"
 	"os"
 	"path/filepath"
-	"regexp"
 	"runtime"
 	"strings"
 	"testing"
 	"time"
 
 	"cmd/go/internal/cfg"
+	"cmd/go/internal/gover"
 	"cmd/go/internal/script"
 	"cmd/go/internal/script/scripttest"
 	"cmd/go/internal/vcweb/vcstest"
@@ -209,10 +207,6 @@
 	if err != nil {
 		return nil, err
 	}
-	version, err := goVersion()
-	if err != nil {
-		return nil, err
-	}
 	env := []string{
 		pathEnvName() + "=" + testBin + string(filepath.ListSeparator) + os.Getenv(pathEnvName()),
 		homeEnvName() + "=/no-home",
@@ -243,7 +237,7 @@
 		"GONOSUMDB=",
 		"GOVCS=*:all",
 		"devnull=" + os.DevNull,
-		"goversion=" + version,
+		"goversion=" + gover.Local(),
 		"CMDGO_TEST_RUN_MAIN=true",
 		"HGRCPATH=",
 		"GOTOOLCHAIN=auto",
@@ -281,16 +275,6 @@
 	return env, nil
 }
 
-// goVersion returns the current Go version.
-func goVersion() (string, error) {
-	tags := build.Default.ReleaseTags
-	version := tags[len(tags)-1]
-	if !regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`).MatchString(version) {
-		return "", fmt.Errorf("invalid go version %q", version)
-	}
-	return version[2:], nil
-}
-
 var extraEnvKeys = []string{
 	"SYSTEMROOT",         // must be preserved on Windows to find DLLs; golang.org/issue/25210
 	"WINDIR",             // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 77c6081..ff8daad 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -3880,7 +3880,7 @@
 	case 29: /* op Rn, Rd */
 		fc := c.aclass(&p.From)
 		tc := c.aclass(&p.To)
-		if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZREG || tc == C_REG) {
+		if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZREG || tc == C_REG || tc == C_ZREG) {
 			// FMOV Rx, Fy or FMOV Fy, Rx
 			o1 = FPCVTI(0, 0, 0, 0, 6)
 			if p.As == AFMOVD {
diff --git a/src/crypto/x509/platform_root_cert.pem b/src/crypto/x509/platform_root_cert.pem
new file mode 100644
index 0000000..bef31f4
--- /dev/null
+++ b/src/crypto/x509/platform_root_cert.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB/DCCAaOgAwIBAgICIzEwCgYIKoZIzj0EAwIwLDEqMCgGA1UEAxMhR28gcGxh
+dGZvcm0gdmVyaWZpZXIgdGVzdGluZyByb290MB4XDTIzMDUyNjE3NDQwMVoXDTI4
+MDUyNDE4NDQwMVowLDEqMCgGA1UEAxMhR28gcGxhdGZvcm0gdmVyaWZpZXIgdGVz
+dGluZyByb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5dNQY4FY29i2g3xx
+7FyH4XiZz0C0AM4uyPUsXCZNb7CsctHDLhLtzABWSfFz76j+oVhq+qKrwIHsLX+7
+f6YTQqOBtDCBsTAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUEJInRbtQR6xTUSwvtdAe9A4XHwQw
+WgYDVR0eAQH/BFAwTqAaMBiCFnRlc3RpbmcuZ29sYW5nLmludmFsaWShMDAKhwgA
+AAAAAAAAADAihyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAKBggq
+hkjOPQQDAgNHADBEAiBgzgLyQm4rK1AuIcElH3MdRqlteq3nzZCxKOI4xHXYjQIg
+BCSzaCb1+/AK+mhRubrdebFYlUdveTH98wAfKQHaw64=
+-----END CERTIFICATE-----
diff --git a/src/crypto/x509/platform_root_key.pem b/src/crypto/x509/platform_root_key.pem
new file mode 100644
index 0000000..c0b6eeb
--- /dev/null
+++ b/src/crypto/x509/platform_root_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIHhv8LVzb9gqJzAY0P442+FW0oqbfBrLnfqxyyAujOFSoAoGCCqGSM49
+AwEHoUQDQgAE5dNQY4FY29i2g3xx7FyH4XiZz0C0AM4uyPUsXCZNb7CsctHDLhLt
+zABWSfFz76j+oVhq+qKrwIHsLX+7f6YTQg==
+-----END EC PRIVATE KEY-----
diff --git a/src/crypto/x509/platform_test.go b/src/crypto/x509/platform_test.go
new file mode 100644
index 0000000..c35f0b4
--- /dev/null
+++ b/src/crypto/x509/platform_test.go
@@ -0,0 +1,251 @@
+// Copyright 2023 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 x509
+
+//go:generate go run gen_testing_root.go
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"encoding/pem"
+	"math/big"
+	"os"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+)
+
+// In order to run this test suite locally, you need to insert the test root, at
+// the path below, into your trust store. This root is constrained such that it
+// should not be dangerous to local developers to trust, but care should be
+// taken when inserting it into the trust store not to give it increased
+// permissions.
+//
+// On macOS the certificate can be further constrained to only be valid for
+// 'SSL' in the certificate properties pane of the 'Keychain Access' program.
+//
+// On Windows the certificate can also be constrained to only server
+// authentication in the properties pane of the certificate in the
+// "Certificates" snap-in of mmc.exe.
+
+const (
+	rootCertPath = "platform_root_cert.pem"
+	rootKeyPath  = "platform_root_key.pem"
+)
+
+func TestPlatformVerifier(t *testing.T) {
+	if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
+		t.Skip("only tested on windows and darwin")
+	}
+
+	der, err := os.ReadFile(rootCertPath)
+	if err != nil {
+		t.Fatalf("failed to read test root: %s", err)
+	}
+	b, _ := pem.Decode(der)
+	testRoot, err := ParseCertificate(b.Bytes)
+	if err != nil {
+		t.Fatalf("failed to parse test root: %s", err)
+	}
+
+	der, err = os.ReadFile(rootKeyPath)
+	if err != nil {
+		t.Fatalf("failed to read test key: %s", err)
+	}
+	b, _ = pem.Decode(der)
+	testRootKey, err := ParseECPrivateKey(b.Bytes)
+	if err != nil {
+		t.Fatalf("failed to parse test key: %s", err)
+	}
+
+	if _, err := testRoot.Verify(VerifyOptions{}); err != nil {
+		t.Skipf("test root is not in trust store, skipping (err: %q)", err)
+	}
+
+	now := time.Now()
+
+	tests := []struct {
+		name       string
+		cert       *Certificate
+		selfSigned bool
+		dnsName    string
+		time       time.Time
+		eku        []ExtKeyUsage
+
+		expectedErr string
+		windowsErr  string
+		macosErr    string
+	}{
+		{
+			name: "valid",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+		},
+		{
+			name: "valid (with name)",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			dnsName: "valid.testing.golang.invalid",
+		},
+		{
+			name: "valid (with time)",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			time: now.Add(time.Minute * 30),
+		},
+		{
+			name: "valid (with eku)",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			eku: []ExtKeyUsage{ExtKeyUsageServerAuth},
+		},
+		{
+			name: "wrong name",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			dnsName:     "invalid.testing.golang.invalid",
+			expectedErr: "x509: certificate is valid for valid.testing.golang.invalid, not invalid.testing.golang.invalid",
+		},
+		{
+			name: "expired (future)",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			time:        now.Add(time.Hour * 2),
+			expectedErr: "x509: certificate has expired or is not yet valid",
+		},
+		{
+			name: "expired (past)",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			time:        now.Add(time.Hour * 2),
+			expectedErr: "x509: certificate has expired or is not yet valid",
+		},
+		{
+			name: "self-signed",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			selfSigned: true,
+			macosErr:   "x509: “valid.testing.golang.invalid” certificate is not trusted",
+			windowsErr: "x509: certificate signed by unknown authority",
+		},
+		{
+			name: "non-specified KU",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageServerAuth},
+			},
+			eku:         []ExtKeyUsage{ExtKeyUsageEmailProtection},
+			expectedErr: "x509: certificate specifies an incompatible key usage",
+		},
+		{
+			name: "non-nested KU",
+			cert: &Certificate{
+				SerialNumber: big.NewInt(1),
+				DNSNames:     []string{"valid.testing.golang.invalid"},
+				NotBefore:    now.Add(-time.Hour),
+				NotAfter:     now.Add(time.Hour),
+				ExtKeyUsage:  []ExtKeyUsage{ExtKeyUsageEmailProtection},
+			},
+			macosErr:   "x509: “valid.testing.golang.invalid” certificate is not permitted for this usage",
+			windowsErr: "x509: certificate specifies an incompatible key usage",
+		},
+	}
+
+	leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		t.Fatalf("ecdsa.GenerateKey failed: %s", err)
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			parent := testRoot
+			if tc.selfSigned {
+				parent = tc.cert
+			}
+			certDER, err := CreateCertificate(rand.Reader, tc.cert, parent, leafKey.Public(), testRootKey)
+			if err != nil {
+				t.Fatalf("CreateCertificate failed: %s", err)
+			}
+			cert, err := ParseCertificate(certDER)
+			if err != nil {
+				t.Fatalf("ParseCertificate failed: %s", err)
+			}
+
+			var opts VerifyOptions
+			if tc.dnsName != "" {
+				opts.DNSName = tc.dnsName
+			}
+			if !tc.time.IsZero() {
+				opts.CurrentTime = tc.time
+			}
+			if len(tc.eku) > 0 {
+				opts.KeyUsages = tc.eku
+			}
+
+			expectedErr := tc.expectedErr
+			if runtime.GOOS == "darwin" && tc.macosErr != "" {
+				expectedErr = tc.macosErr
+			} else if runtime.GOOS == "windows" && tc.windowsErr != "" {
+				expectedErr = tc.windowsErr
+			}
+
+			_, err = cert.Verify(opts)
+			if err != nil && expectedErr == "" {
+				t.Errorf("unexpected verification error: %s", err)
+			} else if err != nil && !strings.HasPrefix(err.Error(), expectedErr) {
+				t.Errorf("unexpected verification error: got %q, want %q", err.Error(), expectedErr)
+			} else if err == nil && expectedErr != "" {
+				t.Errorf("unexpected verification success: want %q", expectedErr)
+			}
+		})
+	}
+}
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index fb60c17..fd7db65 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -361,19 +361,27 @@
 	// mark top of stack before any variables in the body are pushed.
 	mark := s.mark()
 	oneIteration := func(index, elem reflect.Value) {
-		// Set top var (lexically the second if there are two) to the element.
 		if len(r.Pipe.Decl) > 0 {
 			if r.Pipe.IsAssign {
-				s.setVar(r.Pipe.Decl[0].Ident[0], elem)
+				// With two variables, index comes first.
+				// With one, we use the element.
+				if len(r.Pipe.Decl) > 1 {
+					s.setVar(r.Pipe.Decl[0].Ident[0], index)
+				} else {
+					s.setVar(r.Pipe.Decl[0].Ident[0], elem)
+				}
 			} else {
+				// Set top var (lexically the second if there
+				// are two) to the element.
 				s.setTopVar(1, elem)
 			}
 		}
-		// Set next var (lexically the first if there are two) to the index.
 		if len(r.Pipe.Decl) > 1 {
 			if r.Pipe.IsAssign {
-				s.setVar(r.Pipe.Decl[1].Ident[0], index)
+				s.setVar(r.Pipe.Decl[1].Ident[0], elem)
 			} else {
+				// Set next var (lexically the first if there
+				// are two) to the index.
 				s.setTopVar(2, index)
 			}
 		}
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index 40e3c5e..6eb0d41 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -694,6 +694,7 @@
 	{"bug18c", "{{eq . 'P'}}", "true", 'P', true},
 
 	{"issue56490", "{{$i := 0}}{{$x := 0}}{{range $i = .AI}}{{end}}{{$i}}", "5", tVal, true},
+	{"issue60801", "{{$k := 0}}{{$v := 0}}{{range $k, $v = .AI}}{{$k}}={{$v}} {{end}}", "0=3 1=4 2=5 ", tVal, true},
 }
 
 func zeroArgs() string {
diff --git a/test/codegen/math.go b/test/codegen/math.go
index e630530..6a7d304 100644
--- a/test/codegen/math.go
+++ b/test/codegen/math.go
@@ -57,6 +57,7 @@
 	// mips64/hardfloat:"SQRTD" mips64/softfloat:-"SQRTD"
 	// wasm:"F64Sqrt"
 	// ppc64x:"FSQRT"
+	// riscv64: "FSQRTD"
 	return math.Sqrt(x)
 }
 
@@ -69,6 +70,7 @@
 	// mips64/hardfloat:"SQRTF" mips64/softfloat:-"SQRTF"
 	// wasm:"F32Sqrt"
 	// ppc64x:"FSQRTS"
+	// riscv64: "FSQRTS"
 	return float32(math.Sqrt(float64(x)))
 }