fix: canonicalize generic field objects for struct hashing

Use `types.Var.Origin()` when mapping and hashing struct fields so selector and
declaration sites share the same canonical field identity. This fixes build
failures for generic aliases and named types over those aliases.

Fixes #924
This commit is contained in:
Paul Scheduikat
2026-03-01 16:30:56 +01:00
committed by Daniel Martí
parent 1b6cc14d93
commit cb98b4daab
4 changed files with 32 additions and 14 deletions
+8 -8
View File
@@ -142,25 +142,25 @@ func debugDirNeedsRebuild() (bool, error) {
return false, err
}
sawBuildInputs := false
missingArtifacts := false
for _, lpkg := range sharedCache.ListedPackages {
if len(lpkg.GarbleActionID) == 0 {
continue
}
if len(lpkg.CompiledGoFiles) > 0 {
sawBuildInputs = true
if debugArtifactsExistForPkg(fsCache, lpkg, debugCacheKindCompile) {
return false, nil
if !debugArtifactsExistForPkg(fsCache, lpkg, debugCacheKindCompile) {
missingArtifacts = true
}
}
if len(lpkg.SFiles) > 0 {
sawBuildInputs = true
if debugArtifactsExistForPkg(fsCache, lpkg, debugCacheKindAsm) {
return false, nil
if !debugArtifactsExistForPkg(fsCache, lpkg, debugCacheKindAsm) {
missingArtifacts = true
}
}
}
// If no debug artifacts exist yet, force one full rebuild to warm cache.
// Once at least one cache entry exists, incremental builds can repopulate
// from cache + newly rebuilt packages without forcing -a.
return sawBuildInputs, nil
// For -debugdir to be complete, we either need artifacts in cache for every
// package input, or we need to force one full rebuild with -a.
return sawBuildInputs && missingArtifacts, nil
}
+6 -2
View File
@@ -103,11 +103,15 @@ One can reverse a captured panic stack trace as follows:
if obj == nil || !obj.IsField() {
continue
}
strct := fieldToStruct[obj]
originObj := obj.Origin()
strct := fieldToStruct[originObj]
if strct == nil {
strct = fieldToStruct[obj]
}
if strct == nil {
panic("could not find struct for field " + name.Name)
}
replaces = append(replaces, hashWithStruct(strct, obj), name.Name)
replaces = append(replaces, hashWithStruct(strct, originObj), name.Name)
}
case *ast.CallExpr:
+12
View File
@@ -16,6 +16,11 @@ func main() {
g2 := GenericGraph[*[]byte]{Content: new([]byte)}
g2.Edges = make([]GenericGraph[*[]byte], 1)
var ga genericAlias
ga.list = nil
var gan genericAliasNamed
gan.list = nil
}
func GenericFunc[GenericParamA, B any](x GenericParamA, y B) {}
@@ -66,3 +71,10 @@ func (w *AsyncResult[oldT, newT]) AsResult() Result[newT] {
}
func (w *AsyncResult[oldT, newT]) Redirect(struct {ret newT}) {}
type genericAlias = generic[int]
type generic[T any] struct {
list *T
}
type genericAliasNamed genericAlias
+6 -4
View File
@@ -166,10 +166,11 @@ func recordType(used, origin types.Type, done map[*types.Named]bool, fieldToStru
origin := origin.(*types.Struct)
for i := range used.NumFields() {
field := used.Field(i)
fieldToStruct[field] = origin
fieldToStruct[field.Origin()] = origin
if field.Embedded() {
recordType(field.Type(), origin.Field(i).Type(), done, fieldToStruct)
originField := origin.Field(i)
recordType(field.Type(), originField.Type(), done, fieldToStruct)
}
}
}
@@ -1237,11 +1238,12 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
// any field is unexported. If that is done, add a test
// that ensures unexported fields from different
// packages result in different obfuscated names.
strct := tf.fieldToStruct[obj]
originObj := obj.Origin()
strct := tf.fieldToStruct[originObj]
if strct == nil {
panic("could not find struct for field " + name)
}
node.Name = hashWithStruct(strct, obj)
node.Name = hashWithStruct(strct, originObj)
if flagDebug { // TODO(mvdan): remove once https://go.dev/issue/53465 if fixed
log.Printf("%s %q hashed with struct fields to %q", debugName, name, node.Name)
}