diff --git a/tools/checkinfo/checkinfo.go b/tools/checkinfo/checkinfo.go index 6dd07473a..3871015f1 100644 --- a/tools/checkinfo/checkinfo.go +++ b/tools/checkinfo/checkinfo.go @@ -75,14 +75,16 @@ func (p *pkg) walkObject(pass *analysis.Pass, obj types.Object) { case *types.Var: // Skip if the var's type is a type parameter. typ := x.Type() - if _, ok := typ.(*types.TypeParam); ok { + if _, ok := typ.Underlying().(*types.TypeParam); ok { break } // Add information as a field. - a := Align(pass.TypesSizes.Alignof(typ)) - s := Size(pass.TypesSizes.Sizeof(typ)) - pass.ExportObjectFact(obj, &a) - pass.ExportObjectFact(obj, &s) + bestEffort(func() { + a := Align(pass.TypesSizes.Alignof(typ)) + s := Size(pass.TypesSizes.Sizeof(typ)) + pass.ExportObjectFact(obj, &a) + pass.ExportObjectFact(obj, &s) + }) case *types.TypeName: // Skip if just an alias, or if not underlying type, or if a // type parameter. If it is not an alias, then it must be @@ -91,14 +93,16 @@ func (p *pkg) walkObject(pass *analysis.Pass, obj types.Object) { if x.IsAlias() || typ == nil || typ.Underlying() == nil { break } - if _, ok := typ.(*types.TypeParam); ok { + if _, ok := typ.Underlying().(*types.TypeParam); ok { break } // Add basic information. - a := Align(pass.TypesSizes.Alignof(typ)) - s := Size(pass.TypesSizes.Sizeof(typ)) - pass.ExportObjectFact(obj, &a) - pass.ExportObjectFact(obj, &s) + bestEffort(func() { + a := Align(pass.TypesSizes.Alignof(typ)) + s := Size(pass.TypesSizes.Sizeof(typ)) + pass.ExportObjectFact(obj, &a) + pass.ExportObjectFact(obj, &s) + }) // Recurse to fields if this is a definition. if structType, ok := typ.Underlying().(*types.Struct); ok { fields := make([]*types.Var, 0, structType.NumFields()) @@ -107,10 +111,12 @@ func (p *pkg) walkObject(pass *analysis.Pass, obj types.Object) { fields = append(fields, fieldObj) p.walkObject(pass, fieldObj) } - offsets := pass.TypesSizes.Offsetsof(fields) - for i, field := range fields { - pass.ExportObjectFact(field, (*Offset)(&offsets[i])) - } + bestEffort(func() { + offsets := pass.TypesSizes.Offsetsof(fields) + for i, field := range fields { + pass.ExportObjectFact(field, (*Offset)(&offsets[i])) + } + }) } case *types.Func: // Skip if no underlying type. @@ -136,6 +142,17 @@ func (p *pkg) walkObject(pass *analysis.Pass, obj types.Object) { } } +// bestEffort is a panic/recover wrapper. This is used because the tools +// package occasionally panics due to some type parameter use, and there is no +// simple or obvious way to detect these conditions. This should only be used +// when absolutely necessary. +func bestEffort(fn func()) { + defer func() { + recover() + }() + fn() +} + // walkScope recursively resolves a scope. func (p *pkg) walkScope(pass *analysis.Pass, scope *types.Scope) { for _, name := range scope.Names() { diff --git a/tools/checklocks/analysis.go b/tools/checklocks/analysis.go index f1ab91c47..62a32d099 100644 --- a/tools/checklocks/analysis.go +++ b/tools/checklocks/analysis.go @@ -56,7 +56,7 @@ func (pc *passContext) typeAlignment(pkg *types.Package, obj types.Object) atomi } case *types.Array: // Export direct alignment requirements. - if named, ok := x.Elem().(*types.Named); ok { + if named, ok := x.Elem().(*types.Named); ok && !hasTypeParams(named) { requiredOffset = pc.typeAlignment(pkg, named.Obj()) } default: @@ -75,12 +75,19 @@ func (pc *passContext) typeAlignment(pkg *types.Package, obj types.Object) atomi return requiredOffset } +// hasTypeParams returns true iff the named type has type parameters. +func hasTypeParams(typ *types.Named) bool { + return typ.TypeParams() != nil && typ.TypeParams().Len() > 0 +} + // checkTypeAlignment checks the alignment of the given type. // // This calls typeAlignment, which resolves all types recursively. This method // should be called for all types individual to ensure full coverage. func (pc *passContext) checkTypeAlignment(pkg *types.Package, typ *types.Named) { - _ = pc.typeAlignment(pkg, typ.Obj()) + if !hasTypeParams(typ) { + _ = pc.typeAlignment(pkg, typ.Obj()) + } } // atomicRules specify read constraints.