mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
checklocks attaches lock facts to the declaration objects produced by
go/types, but SSA analysis of generic code can observe instantiated
`*types.Var/*types.Func` objects instead. Importing facts from those
instantiated objects fails, which causes spurious inference warnings
("may require checklocks annotation...") and can miss expected
checklocksfail reports.
Fix this by falling back to Origin() when importing lock guard and
function facts, and by normalizing inference observation keys to the
origin object so inference uses the same fact identity as export.
Add regression coverage for generic methods and functions, including a
cross-package generics.
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
// Copyright 2026 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package test
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"gvisor.dev/gvisor/tools/checklocks/test/crosspkg"
|
|
)
|
|
|
|
var genericGlobalMu sync.Mutex
|
|
|
|
type genericValueStruct[T any] struct {
|
|
mu sync.Mutex
|
|
// +checklocks:mu
|
|
value T
|
|
}
|
|
|
|
func (g *genericValueStruct[T]) set(v T) {
|
|
g.mu.Lock()
|
|
g.value = v
|
|
g.mu.Unlock()
|
|
}
|
|
|
|
func (g *genericValueStruct[T]) setUnlocked(v T) {
|
|
g.value = v // +checklocksfail
|
|
}
|
|
|
|
// +checklocks:g.mu
|
|
func (g *genericValueStruct[T]) setLocked(v T) {
|
|
g.value = v
|
|
}
|
|
|
|
// genericMapStruct is a minimal reproduction of generic guarded fields.
|
|
// See https://github.com/google/gvisor/issues/10372 and #11671.
|
|
type genericMapStruct[K comparable] struct {
|
|
mu sync.Mutex
|
|
// +checklocks:mu
|
|
m map[K]struct{}
|
|
}
|
|
|
|
func (g *genericMapStruct[K]) add(k K) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
g.m[k] = struct{}{}
|
|
}
|
|
|
|
// genericUnlockedWrite should fail: writing without holding the guard.
|
|
func genericUnlockedWrite[K comparable](g *genericMapStruct[K], k K) {
|
|
g.m[k] = struct{}{} // +checklocksfail
|
|
}
|
|
|
|
// +checklocks:genericGlobalMu
|
|
func genericNeedsGlobalLock[T any](v T) {
|
|
_ = v
|
|
}
|
|
|
|
func genericCallsWithoutLock[T any](v T) {
|
|
genericNeedsGlobalLock(v) // +checklocksfail
|
|
}
|
|
|
|
func genericCallsWithLock[T any](v T) {
|
|
genericGlobalMu.Lock()
|
|
genericNeedsGlobalLock(v)
|
|
genericGlobalMu.Unlock()
|
|
}
|
|
|
|
// crossPkgGenericValid ensures that lock guard facts on generic types imported
|
|
// from another package are still found when analyzing a generic use site.
|
|
func crossPkgGenericValid[T any](g *crosspkg.GenericGuard[T], v T) {
|
|
g.Mu.Lock()
|
|
g.Value = v
|
|
g.Mu.Unlock()
|
|
}
|
|
|
|
// crossPkgGenericInvalidWrite should fail: writing without holding the guard.
|
|
func crossPkgGenericInvalidWrite[T any](g *crosspkg.GenericGuard[T], v T) {
|
|
g.Value = v // +checklocksfail
|
|
}
|