Repository: tetratelabs/wazero
Pull request #2301 (2024-08-13 21:54:41)

ci: upgrade to Go 1.23
As Go 1.23 has been released now, this updates the Go version used in the CI.
Also, this updates our floor Go version to 1.21 following our support policy.

https://go.dev/blog/go1.23

ncruces: I also had a look at https://go.dev/blog/go1.21: we can now use `clear`, which I did.

`reflect.SliceHeader` is ever more deprecated, which linters complain about; silenced some, fixed one.

Deleted some code that was `!go1.21`.


Patch:
diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml
index a31fe231..01cd1a3f 100644
--- a/.github/workflows/commit.yaml
+++ b/.github/workflows/commit.yaml
@@ -14,7 +14,7 @@ on:
       - 'netlify.toml'
 
 env:  # Update this prior to requiring a higher minor version in go.mod
-  GO_VERSION: "1.22"
+  GO_VERSION: "1.23"
 
 defaults:
   run:  # use bash for all operating systems unless overridden
@@ -51,7 +51,7 @@ jobs:
             ~/.cache/golangci-lint
             ~/go/pkg/mod
             ~/go/bin
-          key: check-${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum', 'Makefile') }}
+          key: check-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum', 'Makefile') }}
 
       - run: make build.spectest
 
@@ -71,9 +71,8 @@ jobs:
         - os: macos-14
           arch: arm64
         go-version:
-          - "1.23.0-rc.2"  # Test the pending next version
-          - "1.22"  # Current Go version
-          - "1.20"  # Floor Go version of wazero (current - 2)
+          - "1.23"  # Current Go version
+          - "1.21"  # Floor Go version of wazero (current - 2)
 
     steps:
 
@@ -113,9 +112,8 @@ jobs:
       fail-fast: false  # don't fail fast as sometimes failures are arch/OS specific
       matrix:  # Use versions consistent with wazero's Go support policy.
         go-version:
-          - "1.23.0-rc.2"  # Test the pending next version
-          - "1.22"  # Current Go version
-          - "1.20"  # Floor Go version of wazero (current - 2)
+          - "1.23"  # Current Go version
+          - "1.21"  # Floor Go version of wazero (current - 2)
         arch:
           - "amd64"
           - "arm64"
@@ -160,7 +158,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - uses: actions/setup-go@v2
-        with:
+        with:  # Use version consistent with TinyGo.
           go-version: "1.22"
       - uses: acifani/setup-tinygo@v2
         with:
diff --git a/.github/workflows/examples.yaml b/.github/workflows/examples.yaml
index 36fecedb..40b6729d 100644
--- a/.github/workflows/examples.yaml
+++ b/.github/workflows/examples.yaml
@@ -35,7 +35,7 @@ jobs:
       matrix:  # Use versions consistent with TinyGo.
         go-version:
           - "1.22"
-          - "1.20"
+          - "1.21"
 
     steps:
       - name: Checkout
@@ -84,7 +84,6 @@ jobs:
 
       - name: Build TinyGo examples
         run: make build.examples.tinygo
-        if: matrix.go-version != '1.20' # fails with TinyGo v0.32.0
 
       - name: Build AssemblyScript examples
         run: make build.examples.as
diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml
index 61c5158e..7b3fd6fa 100644
--- a/.github/workflows/integration.yaml
+++ b/.github/workflows/integration.yaml
@@ -18,7 +18,7 @@ defaults:
     shell: bash
 
 env:  # Update this prior to requiring a higher minor version in go.mod
-  GO_VERSION: "1.22"
+  GO_VERSION: "1.23"
   TINYGO_VERSION: "0.32.0"
   ZIG_VERSION: "0.11.0"
   BINARYEN_VERSION: "116"
@@ -146,8 +146,8 @@ jobs:
 
       - uses: actions/setup-go@v4
         if: steps.binary-cache.outputs.cache-hit != 'true'
-        with:
-          go-version: ${{ env.GO_VERSION }}
+        with:  # Use version consistent with TinyGo.
+          go-version: "1.22"
 
       - name: Build Test Binaries
         if: steps.binary-cache.outputs.cache-hit != 'true'
@@ -276,9 +276,8 @@ jobs:
           name: Windows
           arch: amd64
         go-version:
+          - "1.23"
           - "1.21"
-          - "1.22"
-          - "1.23.0-rc.2"  # Test the pending next version
 
     steps:
       - id: setup-go
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 31b9efab..129d3ce8 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -11,7 +11,7 @@ on:
     tags: 'v[0-9]+.[0-9]+.[0-9]+**'  # Ex. v0.2.0 v0.2.1-rc2
 
 env: # Update this prior to requiring a higher minor version in go.mod
-  GO_VERSION: "1.22"
+  GO_VERSION: "1.23"
 
 defaults:
   run:  # use bash for all operating systems unless overridden
diff --git a/Makefile b/Makefile
index 0b1663f4..17e264e0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 
-gofumpt       := mvdan.cc/gofumpt@v0.5.0
+gofumpt       := mvdan.cc/gofumpt@v0.6.0
 gosimports    := github.com/rinchsan/gosimports/cmd/gosimports@v0.3.8
-golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2
+golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.0
 asmfmt        := github.com/klauspost/asmfmt/cmd/asmfmt@v1.3.2
 # sync this with netlify.toml!
 hugo          := github.com/gohugoio/hugo@v0.115.2
diff --git a/go.mod b/go.mod
index b6906c65..a95e3b1a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,7 @@
 module github.com/tetratelabs/wazero
 
 // Floor Go version of wazero (current - 2)
-go 1.20
+go 1.21
 
 // All the beta tags are retracted and replaced with "pre" to prevent users
 // from accidentally upgrading into the broken beta 1.
diff --git a/imports/wasi_snapshot_preview1/poll.go b/imports/wasi_snapshot_preview1/poll.go
index d09f3024..4f96af2d 100644
--- a/imports/wasi_snapshot_preview1/poll.go
+++ b/imports/wasi_snapshot_preview1/poll.go
@@ -68,9 +68,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno
 	}
 	outBuf, ok := mem.Read(out, nsubscriptions*32)
 	// zero-out all buffer before writing
-	for i := range outBuf {
-		outBuf[i] = 0
-	}
+	clear(outBuf)
 
 	if !ok {
 		return sys.EFAULT
diff --git a/internal/descriptor/table.go b/internal/descriptor/table.go
index 542958bc..03761e6e 100644
--- a/internal/descriptor/table.go
+++ b/internal/descriptor/table.go
@@ -154,11 +154,6 @@ func (t *Table[Key, Item]) Range(f func(Key, Item) bool) {
 
 // Reset clears the content of the table.
 func (t *Table[Key, Item]) Reset() {
-	for i := range t.masks {
-		t.masks[i] = 0
-	}
-	var zero Item
-	for i := range t.items {
-		t.items[i] = zero
-	}
+	clear(t.masks)
+	clear(t.items)
 }
diff --git a/internal/engine/wazevo/backend/isa/amd64/stack.go b/internal/engine/wazevo/backend/isa/amd64/stack.go
index 4d4687cb..ef823bdb 100644
--- a/internal/engine/wazevo/backend/isa/amd64/stack.go
+++ b/internal/engine/wazevo/backend/isa/amd64/stack.go
@@ -12,6 +12,7 @@ func stackView(rbp, top uintptr) []byte {
 	l := int(top - rbp)
 	var stackBuf []byte
 	{
+		//nolint:staticcheck
 		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&stackBuf))
 		hdr.Data = rbp
 		hdr.Len = l
@@ -75,7 +76,7 @@ func GoCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
 	//                 (low address)
 	data := unsafe.Add(unsafe.Pointer(stackPointerBeforeGoCall), 8)
 	size := *stackPointerBeforeGoCall / 8
-	return unsafe.Slice((*uint64)(data), int(size))
+	return unsafe.Slice((*uint64)(data), size)
 }
 
 func AdjustClonedStack(oldRsp, oldTop, rsp, rbp, top uintptr) {
diff --git a/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
index 3ab2d0f1..a72b86f6 100644
--- a/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
+++ b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
@@ -14,6 +14,7 @@ func UnwindStack(sp, _, top uintptr, returnAddresses []uintptr) []uintptr {
 
 	var stackBuf []byte
 	{
+		//nolint:staticcheck
 		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&stackBuf))
 		hdr.Data = sp
 		hdr.Len = l
@@ -77,13 +78,7 @@ func GoCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
 	//              +-----------------+ <---- stackPointerBeforeGoCall
 	//                 (low address)
 	ptr := unsafe.Pointer(stackPointerBeforeGoCall)
+	data := (*uint64)(unsafe.Add(ptr, 16)) // skips the (frame_size, sliceSize).
 	size := *(*uint64)(unsafe.Add(ptr, 8))
-	var view []uint64
-	{
-		sh := (*reflect.SliceHeader)(unsafe.Pointer(&view))
-		sh.Data = uintptr(unsafe.Add(ptr, 16)) // skips the (frame_size, sliceSize).
-		sh.Len = int(size)
-		sh.Cap = int(size)
-	}
-	return view
+	return unsafe.Slice(data, size)
 }
diff --git a/internal/engine/wazevo/backend/regalloc/regset.go b/internal/engine/wazevo/backend/regalloc/regset.go
index 35c20e84..ce84c9c0 100644
--- a/internal/engine/wazevo/backend/regalloc/regset.go
+++ b/internal/engine/wazevo/backend/regalloc/regset.go
@@ -55,9 +55,7 @@ func newRegInUseSet[I Instr, B Block[I], F Function[I, B]]() regInUseSet[I, B, F
 }
 
 func (rs *regInUseSet[I, B, F]) reset() {
-	for i := range rs {
-		rs[i] = nil
-	}
+	clear(rs[:])
 }
 
 func (rs *regInUseSet[I, B, F]) format(info *RegisterInfo) string { //nolint:unused
diff --git a/internal/engine/wazevo/call_engine.go b/internal/engine/wazevo/call_engine.go
index c56b8bcd..639429a6 100644
--- a/internal/engine/wazevo/call_engine.go
+++ b/internal/engine/wazevo/call_engine.go
@@ -554,6 +554,7 @@ func (c *callEngine) cloneStack(l uintptr) (newSP, newFP, newTop uintptr, newSta
 	// Copy the existing contents in the previous Go-allocated stack into the new one.
 	var prevStackAligned, newStackAligned []byte
 	{
+		//nolint:staticcheck
 		sh := (*reflect.SliceHeader)(unsafe.Pointer(&prevStackAligned))
 		sh.Data = c.stackTop - relSp
 		sh.Len = int(relSp)
@@ -563,6 +564,7 @@ func (c *callEngine) cloneStack(l uintptr) (newSP, newFP, newTop uintptr, newSta
 	{
 		newSP = newTop - relSp
 		newFP = newTop - relFp
+		//nolint:staticcheck
 		sh := (*reflect.SliceHeader)(unsafe.Pointer(&newStackAligned))
 		sh.Data = newSP
 		sh.Len = int(relSp)
diff --git a/internal/engine/wazevo/frontend/sort_id.go b/internal/engine/wazevo/frontend/sort_id.go
index 1296706f..5b055d12 100644
--- a/internal/engine/wazevo/frontend/sort_id.go
+++ b/internal/engine/wazevo/frontend/sort_id.go
@@ -1,5 +1,3 @@
-//go:build go1.21
-
 package frontend
 
 import (
diff --git a/internal/engine/wazevo/frontend/sort_id_old.go b/internal/engine/wazevo/frontend/sort_id_old.go
deleted file mode 100644
index 2e786a16..00000000
--- a/internal/engine/wazevo/frontend/sort_id_old.go
+++ /dev/null
@@ -1,17 +0,0 @@
-//go:build !go1.21
-
-// TODO: delete after the floor Go version is 1.21
-
-package frontend
-
-import (
-	"sort"
-
-	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
-)
-
-func sortSSAValueIDs(IDs []ssa.ValueID) {
-	sort.SliceStable(IDs, func(i, j int) bool {
-		return int(IDs[i]) < int(IDs[j])
-	})
-}
diff --git a/internal/engine/wazevo/hostmodule.go b/internal/engine/wazevo/hostmodule.go
index c01b4fe1..800a5d2a 100644
--- a/internal/engine/wazevo/hostmodule.go
+++ b/internal/engine/wazevo/hostmodule.go
@@ -16,6 +16,7 @@ func buildHostModuleOpaque(m *wasm.Module, listeners []experimental.FunctionList
 	binary.LittleEndian.PutUint64(ret[0:], uint64(uintptr(unsafe.Pointer(m))))
 
 	if len(listeners) > 0 {
+		//nolint:staticcheck
 		sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&listeners))
 		binary.LittleEndian.PutUint64(ret[8:], uint64(sliceHeader.Data))
 		binary.LittleEndian.PutUint64(ret[16:], uint64(sliceHeader.Len))
@@ -33,6 +34,7 @@ func buildHostModuleOpaque(m *wasm.Module, listeners []experimental.FunctionList
 
 func hostModuleFromOpaque(opaqueBegin uintptr) *wasm.Module {
 	var opaqueViewOverSlice []byte
+	//nolint:staticcheck
 	sh := (*reflect.SliceHeader)(unsafe.Pointer(&opaqueViewOverSlice))
 	sh.Data = opaqueBegin
 	sh.Len = 32
@@ -42,6 +44,7 @@ func hostModuleFromOpaque(opaqueBegin uintptr) *wasm.Module {
 
 func hostModuleListenersSliceFromOpaque(opaqueBegin uintptr) []experimental.FunctionListener {
 	var opaqueViewOverSlice []byte
+	//nolint:staticcheck
 	sh := (*reflect.SliceHeader)(unsafe.Pointer(&opaqueViewOverSlice))
 	sh.Data = opaqueBegin
 	sh.Len = 32
@@ -51,6 +54,7 @@ func hostModuleListenersSliceFromOpaque(opaqueBegin uintptr) []experimental.Func
 	l := binary.LittleEndian.Uint64(opaqueViewOverSlice[16:])
 	c := binary.LittleEndian.Uint64(opaqueViewOverSlice[24:])
 	var ret []experimental.FunctionListener
+	//nolint:staticcheck
 	sh = (*reflect.SliceHeader)(unsafe.Pointer(&ret))
 	sh.Data = uintptr(b)
 	sh.Len = int(l)
@@ -63,6 +67,7 @@ func hostModuleGoFuncFromOpaque[T any](index int, opaqueBegin uintptr) T {
 	ptr := opaqueBegin + offset
 
 	var opaqueViewOverFunction []byte
+	//nolint:staticcheck
 	sh := (*reflect.SliceHeader)(unsafe.Pointer(&opaqueViewOverFunction))
 	sh.Data = ptr
 	sh.Len = 16
diff --git a/internal/engine/wazevo/ssa/basic_block_sort.go b/internal/engine/wazevo/ssa/basic_block_sort.go
index e1471edc..fb98298f 100644
--- a/internal/engine/wazevo/ssa/basic_block_sort.go
+++ b/internal/engine/wazevo/ssa/basic_block_sort.go
@@ -1,5 +1,3 @@
-//go:build go1.21
-
 package ssa
 
 import (
diff --git a/internal/engine/wazevo/ssa/basic_block_sort_old.go b/internal/engine/wazevo/ssa/basic_block_sort_old.go
deleted file mode 100644
index 9dc881da..00000000
--- a/internal/engine/wazevo/ssa/basic_block_sort_old.go
+++ /dev/null
@@ -1,24 +0,0 @@
-//go:build !go1.21
-
-// TODO: delete after the floor Go version is 1.21
-
-package ssa
-
-import "sort"
-
-func sortBlocks(blocks []*basicBlock) {
-	sort.SliceStable(blocks, func(i, j int) bool {
-		iBlk, jBlk := blocks[i], blocks[j]
-		if jBlk.ReturnBlock() {
-			return true
-		}
-		if iBlk.ReturnBlock() {
-			return false
-		}
-		iRoot, jRoot := iBlk.rootInstr, jBlk.rootInstr
-		if iRoot == nil || jRoot == nil { // For testing.
-			return true
-		}
-		return iBlk.rootInstr.id < jBlk.rootInstr.id
-	})
-}
diff --git a/internal/engine/wazevo/wazevoapi/resetmap.go b/internal/engine/wazevo/wazevoapi/resetmap.go
index 7177fbb4..3fc7aa14 100644
--- a/internal/engine/wazevo/wazevoapi/resetmap.go
+++ b/internal/engine/wazevo/wazevoapi/resetmap.go
@@ -5,9 +5,7 @@ func ResetMap[K comparable, V any](m map[K]V) map[K]V {
 	if m == nil {
 		m = make(map[K]V)
 	} else {
-		for v := range m {
-			delete(m, v)
-		}
+		clear(m)
 	}
 	return m
 }
diff --git a/internal/wasm/memory.go b/internal/wasm/memory.go
index 1a23dc46..8e072fd1 100644
--- a/internal/wasm/memory.go
+++ b/internal/wasm/memory.go
@@ -305,6 +305,7 @@ func PagesToUnitOfBytes(pages uint32) string {
 
 // Uses atomic write to update the length of a slice.
 func atomicStoreLengthAndCap(slice *[]byte, length uintptr, cap uintptr) {
+	//nolint:staticcheck
 	slicePtr := (*reflect.SliceHeader)(unsafe.Pointer(slice))
 	capPtr := (*uintptr)(unsafe.Pointer(&slicePtr.Cap))
 	atomic.StoreUintptr(capPtr, cap)
@@ -314,6 +315,7 @@ func atomicStoreLengthAndCap(slice *[]byte, length uintptr, cap uintptr) {
 
 // Uses atomic write to update the length of a slice.
 func atomicStoreLength(slice *[]byte, length uintptr) {
+	//nolint:staticcheck
 	slicePtr := (*reflect.SliceHeader)(unsafe.Pointer(slice))
 	lenPtr := (*uintptr)(unsafe.Pointer(&slicePtr.Len))
 	atomic.StoreUintptr(lenPtr, length)
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index cf87c30e..dda6e5b6 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -3,6 +3,7 @@ package wasm
 import (
 	"context"
 	"encoding/binary"
+	"errors"
 	"fmt"
 	"sync"
 	"sync/atomic"
@@ -659,20 +660,20 @@ func (s *Store) GetFunctionTypeID(t *FunctionType) (FunctionTypeID, error) {
 }
 
 // CloseWithExitCode implements the same method as documented on wazero.Runtime.
-func (s *Store) CloseWithExitCode(ctx context.Context, exitCode uint32) (err error) {
+func (s *Store) CloseWithExitCode(ctx context.Context, exitCode uint32) error {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 	// Close modules in reverse initialization order.
+	var errs []error
 	for m := s.moduleList; m != nil; m = m.next {
 		// If closing this module errs, proceed anyway to close the others.
-		if e := m.closeWithExitCode(ctx, exitCode); e != nil && err == nil {
-			// TODO: use multiple errors handling in Go 1.20.
-			err = e // first error
+		if err := m.closeWithExitCode(ctx, exitCode); err != nil {
+			errs = append(errs, err)
 		}
 	}
 	s.moduleList = nil
 	s.nameToModule = nil
 	s.nameToModuleCap = 0
 	s.typeIDs = nil
-	return
+	return errors.Join(errs...)
 }
diff --git a/internal/wasmdebug/dwarf.go b/internal/wasmdebug/dwarf.go
index 3b0d3a7a..50ba7b2b 100644
--- a/internal/wasmdebug/dwarf.go
+++ b/internal/wasmdebug/dwarf.go
@@ -171,7 +171,6 @@ entry:
 	// Advance the line reader for the found position.
 	lineReader.Seek(ln.pos)
 	err = lineReader.Next(&le)
-
 	if err != nil {
 		// If we reach this block, that means there's a bug in the []line creation logic above.
 		panic("BUG: stored dwarf.LineReaderPos is invalid")
diff --git a/sys/stat_unsupported.go b/sys/stat_unsupported.go
index 583c2adb..cc37012c 100644
--- a/sys/stat_unsupported.go
+++ b/sys/stat_unsupported.go
@@ -7,9 +7,6 @@ import "io/fs"
 // sysParseable is only used here as we define "supported" as being able to
 // parse `info.Sys()`. The above `go:build` constraints exclude 32-bit until
 // that's requested.
-//
-// TODO: When Go 1.21 is out, use the "unix" build constraint (as 1.21 makes
-// our floor Go version 1.19.
 const sysParseable = false
 
 func statFromFileInfo(info fs.FileInfo) Stat_t {


Test patch:
diff --git a/experimental/close_test.go b/experimental/close_test.go
index 5c3ad817..9d88ed89 100644
--- a/experimental/close_test.go
+++ b/experimental/close_test.go
@@ -9,8 +9,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func TestWithCloseNotifier(t *testing.T) {
 	tests := []struct {
diff --git a/experimental/logging/log_listener_test.go b/experimental/logging/log_listener_test.go
index 284cbb94..2cc953ed 100644
--- a/experimental/logging/log_listener_test.go
+++ b/experimental/logging/log_listener_test.go
@@ -15,8 +15,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func Test_loggingListener(t *testing.T) {
 	wasiFuncName := wasi.RandomGetName
diff --git a/experimental/sock/sock_test.go b/experimental/sock/sock_test.go
index d81be2d3..f14b8ab2 100644
--- a/experimental/sock/sock_test.go
+++ b/experimental/sock/sock_test.go
@@ -9,8 +9,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func TestWithSockConfig(t *testing.T) {
 	tests := []struct {
diff --git a/experimental/wazerotest/wazerotest.go b/experimental/wazerotest/wazerotest.go
index 9182b551..149c907b 100644
--- a/experimental/wazerotest/wazerotest.go
+++ b/experimental/wazerotest/wazerotest.go
@@ -379,9 +379,7 @@ func (f *Function) Call(ctx context.Context, params ...uint64) ([]uint64, error)
 	copy(stack, params)
 	err := f.CallWithStack(ctx, stack)
 	if err != nil {
-		for i := range stack {
-			stack[i] = 0
-		}
+		clear(stack)
 	}
 	return stack[:len(f.ResultTypes)], err
 }
diff --git a/imports/assemblyscript/assemblyscript_test.go b/imports/assemblyscript/assemblyscript_test.go
index 04bb74db..71b4736c 100644
--- a/imports/assemblyscript/assemblyscript_test.go
+++ b/imports/assemblyscript/assemblyscript_test.go
@@ -24,8 +24,10 @@ import (
 	"github.com/tetratelabs/wazero/sys"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func TestAbort(t *testing.T) {
 	tests := []struct {
diff --git a/imports/emscripten/emscripten_test.go b/imports/emscripten/emscripten_test.go
index 5bad84a1..3ce7eabf 100644
--- a/imports/emscripten/emscripten_test.go
+++ b/imports/emscripten/emscripten_test.go
@@ -35,8 +35,10 @@ var growWasm []byte
 //go:embed testdata/invoke.wasm
 var invokeWasm []byte
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 // TestGrow is an integration test until we have an Emscripten example.
 func TestGrow(t *testing.T) {
diff --git a/imports/wasi_snapshot_preview1/wasi_test.go b/imports/wasi_snapshot_preview1/wasi_test.go
index 7b250b29..4808dcc9 100644
--- a/imports/wasi_snapshot_preview1/wasi_test.go
+++ b/imports/wasi_snapshot_preview1/wasi_test.go
@@ -18,8 +18,10 @@ import (
 	"github.com/tetratelabs/wazero/sys"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 const testMemoryPageSize = 1
 
diff --git a/internal/engine/interpreter/interpreter_test.go b/internal/engine/interpreter/interpreter_test.go
index 6ac98909..f7606a31 100644
--- a/internal/engine/interpreter/interpreter_test.go
+++ b/internal/engine/interpreter/interpreter_test.go
@@ -12,8 +12,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func TestInterpreter_peekValues(t *testing.T) {
 	ce := &callEngine{}
diff --git a/internal/engine/wazevo/call_engine_test.go b/internal/engine/wazevo/call_engine_test.go
index 9d790801..d3b23cf1 100644
--- a/internal/engine/wazevo/call_engine_test.go
+++ b/internal/engine/wazevo/call_engine_test.go
@@ -45,6 +45,7 @@ func TestCallEngine_growStack(t *testing.T) {
 
 		var view []byte
 		{
+			//nolint:staticcheck
 			sh := (*reflect.SliceHeader)(unsafe.Pointer(&view))
 			sh.Data = newSP
 			sh.Len = 5
diff --git a/internal/engine/wazevo/engine_test.go b/internal/engine/wazevo/engine_test.go
index 294be6a2..1eb79b10 100644
--- a/internal/engine/wazevo/engine_test.go
+++ b/internal/engine/wazevo/engine_test.go
@@ -189,6 +189,7 @@ func TestCompiledModule_functionIndexOf(t *testing.T) {
 	const executableAddr = 0xaaaa
 	var executable []byte
 	{
+		//nolint:staticcheck
 		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&executable))
 		hdr.Data = executableAddr
 		hdr.Len = 0xffff
diff --git a/internal/integration_test/bench/bench_test.go b/internal/integration_test/bench/bench_test.go
index 5cf46856..5dacbfc6 100644
--- a/internal/integration_test/bench/bench_test.go
+++ b/internal/integration_test/bench/bench_test.go
@@ -14,8 +14,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/platform"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 // caseWasm was compiled from TinyGo testdata/case.go
 //
diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go
index 2f4f5e26..178d61bd 100644
--- a/internal/integration_test/engine/adhoc_test.go
+++ b/internal/integration_test/engine/adhoc_test.go
@@ -88,8 +88,10 @@ func TestEngineInterpreter(t *testing.T) {
 	runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter().WithCloseOnContextDone(true), false)
 }
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 const i32, i64, f32, f64, v128 = wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeV128
 
@@ -406,7 +408,8 @@ func testHostFuncMemory(t *testing.T, r wazero.Runtime) {
 
 // testNestedGoContext ensures context is updated when a function calls another.
 func testNestedGoContext(t *testing.T, r wazero.Runtime) {
-	nestedCtx := context.WithValue(context.Background(), struct{}{}, "nested")
+	type arbitrary struct{}
+	nestedCtx := context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 	importedName := t.Name() + "-imported"
 	importingName := t.Name() + "-importing"
diff --git a/internal/integration_test/fuzz/go.mod b/internal/integration_test/fuzz/go.mod
index f9105bec..581fc6bb 100644
--- a/internal/integration_test/fuzz/go.mod
+++ b/internal/integration_test/fuzz/go.mod
@@ -1,6 +1,6 @@
 module github.com/tetratelabs/wazero/internal/integration_test/fuzz
 
-go 1.20
+go 1.21
 
 require github.com/tetratelabs/wazero v0.0.0
 
diff --git a/internal/version/testdata/go.mod b/internal/version/testdata/go.mod
index 55bc30b1..d922ce0b 100644
--- a/internal/version/testdata/go.mod
+++ b/internal/version/testdata/go.mod
@@ -1,6 +1,6 @@
 module github.com/tetratelabs/wazero/internal/version/testdata
 
-go 1.20
+go 1.21
 
 require github.com/tetratelabs/wazero v0.0.0-20220818123113-1948909ec0b1 // indirect
 
diff --git a/internal/wasm/gofunc_test.go b/internal/wasm/gofunc_test.go
index 8b2e8b8c..802fd530 100644
--- a/internal/wasm/gofunc_test.go
+++ b/internal/wasm/gofunc_test.go
@@ -10,8 +10,10 @@ import (
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
+type arbitrary struct{}
+
 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+var testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 
 func Test_parseGoFunc(t *testing.T) {
 	tests := []struct {
diff --git a/internal/wasm/memory_test.go b/internal/wasm/memory_test.go
index 1d040145..d10c8276 100644
--- a/internal/wasm/memory_test.go
+++ b/internal/wasm/memory_test.go
@@ -776,6 +776,7 @@ func TestMemoryInstance_Write_overflow(t *testing.T) {
 	}
 
 	buf := []byte{16, 0, 0, 4}
+	//nolint:staticcheck
 	header := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
 	header.Len = int(huge)
 	header.Cap = int(huge)
diff --git a/internal/wasm/module_instance_test.go b/internal/wasm/module_instance_test.go
index e36e9dee..cc38244a 100644
--- a/internal/wasm/module_instance_test.go
+++ b/internal/wasm/module_instance_test.go
@@ -267,7 +267,8 @@ func TestModuleInstance_CloseModuleOnCanceledOrTimeout(t *testing.T) {
 		const duration = time.Second
 		ctx, cancel := context.WithTimeout(context.Background(), duration)
 		defer cancel()
-		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, struct{}{}, 1)) // Wrapping arbitrary context.
+		type arbitrary struct{}
+		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, arbitrary{}, "arbitrary")) // Wrapping arbitrary context.
 		time.Sleep(duration * 2)
 		defer done()
 
@@ -285,7 +286,8 @@ func TestModuleInstance_CloseModuleOnCanceledOrTimeout(t *testing.T) {
 	t.Run("cancel", func(t *testing.T) {
 		cc := &ModuleInstance{ModuleName: "test", s: s, Sys: internalsys.DefaultContext(nil)}
 		ctx, cancel := context.WithCancel(context.Background())
-		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, struct{}{}, 1)) // Wrapping arbitrary context.
+		type arbitrary struct{}
+		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, arbitrary{}, "arbitrary")) // Wrapping arbitrary context.
 		cancel()
 		// Make sure nothing panics or otherwise gets weird with redundant call to cancel().
 		cancel()
@@ -312,7 +314,8 @@ func TestModuleInstance_CloseModuleOnCanceledOrTimeout(t *testing.T) {
 		// Wrap the cancel context by timeout.
 		ctx, cancel = context.WithTimeout(ctx, duration)
 		defer cancel()
-		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, struct{}{}, 1)) // Wrapping arbitrary context.
+		type arbitrary struct{}
+		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, arbitrary{}, "arbitrary")) // Wrapping arbitrary context.
 		time.Sleep(duration * 2)
 		defer done()
 
@@ -335,7 +338,8 @@ func TestModuleInstance_CloseModuleOnCanceledOrTimeout(t *testing.T) {
 		ctx, timeoutDone = context.WithTimeout(ctx, time.Second*1000)
 		defer timeoutDone()
 
-		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, struct{}{}, 1)) // Wrapping arbitrary context.
+		type arbitrary struct{}
+		done := cc.CloseModuleOnCanceledOrTimeout(context.WithValue(ctx, arbitrary{}, "arbitrary")) // Wrapping arbitrary context.
 		cancel()
 		defer done()
 
diff --git a/runtime_test.go b/runtime_test.go
index b7fce389..60b280b6 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -19,10 +19,12 @@ import (
 	"github.com/tetratelabs/wazero/sys"
 )
 
+type arbitrary struct{}
+
 var (
 	binaryNamedZero = binaryencoding.EncodeModule(&wasm.Module{NameSection: &wasm.NameSection{ModuleName: "0"}})
 	// testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
-	testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
+	testCtx = context.WithValue(context.Background(), arbitrary{}, "arbitrary")
 )
 
 var _ context.Context = &HostContext{}