Updates for go1.18beta1 (#2831)

* go.mod: update golang.org/x/tools to v0.1.8

Fixes TestGeneratedDoc on go1.18

* TeamCity: bump test matrix

Add 1.18 to test matrix. Remove 1.15 from test matrix and from support range.

* proc,tests: update for regabi on arm64 and 386

Make sure that stacktrace registers always contain the PC register of
the current frame, even though the debug_frame rules might not specify
it on architectures that use a link register.
The PC register is needed to look up loclist entries for variable
evaluation.

* goversion: bump maximum supported Go version to 1.18

* proc: disable asyncpreempt on linux/arm64

Asyncpreempt on linux/arm64 can sometimes restart a sequence of
instructions which will make breakpoint appear to be hit twice in some
cases.
This commit is contained in:
Alessandro Arzilli 2021-12-22 19:26:21 +01:00 committed by GitHub
parent 07bcf0cb13
commit 4a94b3eff2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 167 additions and 110 deletions

@ -35,23 +35,23 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
version = "2020.2"
val targets = arrayOf(
"linux/amd64/1.15",
"linux/amd64/1.16",
"linux/amd64/1.17",
"linux/amd64/1.18",
"linux/amd64/tip",
"linux/386/1.17",
"linux/386/1.18",
"linux/arm64/1.17",
"linux/arm64/1.18",
"linux/arm64/tip",
"windows/amd64/1.17",
"windows/amd64/1.18",
"windows/amd64/tip",
"mac/amd64/1.17",
"mac/amd64/1.18",
"mac/amd64/tip",
"mac/arm64/1.17",
"mac/arm64/1.18",
"mac/arm64/tip"
)

@ -21,10 +21,6 @@ function getgo {
}
if [ "$version" = "gotip" ]; then
# TODO: remove this
if [ "$arch" != "amd64" ]; then
exit 0
fi
echo Building Go from tip
getgo $(curl https://go.dev/VERSION?m=text)
export GOROOT_BOOTSTRAP=$GOROOT

@ -8,10 +8,6 @@ ARCH=$2
TMPDIR=$3
if [ "$GOVERSION" = "gotip" ]; then
# TODO: remove this
if [ "$arch" != "amd64" ]; then
exit 0
fi
bootstrapver=$(curl https://go.dev/VERSION?m=text)
cd $TMPDIR
curl -sSL "https://storage.googleapis.com/golang/$bootstrapver.darwin-$ARCH.tar.gz" | tar -xz

@ -34,7 +34,7 @@ function GetGo($version) {
}
if ($version -eq "gotip") {
Exit 0
#Exit 0
$latest = Invoke-WebRequest -Uri https://golang.org/VERSION?m=text -UseBasicParsing | Select-Object -ExpandProperty Content
GetGo $latest
$env:GOROOT_BOOTSTRAP = $env:GOROOT

2
go.mod

@ -18,6 +18,6 @@ require (
go.starlark.net v0.0.0-20200821142938-949cc6f4b097
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654
golang.org/x/tools v0.1.8-0.20211028023602-8de2a7fd1736
golang.org/x/tools v0.1.8
gopkg.in/yaml.v2 v2.4.0
)

2
go.sum

@ -309,6 +309,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8-0.20211028023602-8de2a7fd1736 h1:cw6nUxdoEN5iEIWYD8aAsTZ8iYjLVNiHAb7xz/80WO4=
golang.org/x/tools v0.1.8-0.20211028023602-8de2a7fd1736/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

@ -8,9 +8,9 @@ import (
var (
MinSupportedVersionOfGoMajor = 1
MinSupportedVersionOfGoMinor = 15
MinSupportedVersionOfGoMinor = 16
MaxSupportedVersionOfGoMajor = 1
MaxSupportedVersionOfGoMinor = 17
MaxSupportedVersionOfGoMinor = 18
goTooOldErr = fmt.Sprintf("Go version %%s is too old for this version of Delve (minimum supported version %d.%d, suppress this error with --check-go-version=false)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
goTooOldWarn = fmt.Sprintf("WARNING: undefined behavior - Go version %%s is too old for this version of Delve (minimum supported version %d.%d)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
dlvTooOldErr = fmt.Sprintf("Version of Delve is too old for Go version %%s (maximum supported version %d.%d, suppress this error with --check-go-version=false)", MaxSupportedVersionOfGoMajor, MaxSupportedVersionOfGoMinor)

@ -266,11 +266,23 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
stopReason = proc.StopAttached
}
tgt, err := proc.NewTarget(dbp, dbp.pid, dbp.memthread, proc.NewTargetConfig{
Path: path,
DebugInfoDirs: debugInfoDirs,
DisableAsyncPreempt: runtime.GOOS == "windows" || runtime.GOOS == "freebsd",
StopReason: stopReason,
CanDump: runtime.GOOS == "linux"})
Path: path,
DebugInfoDirs: debugInfoDirs,
// We disable asyncpreempt for the following reasons:
// - on Windows asyncpreempt is incompatible with debuggers, see:
// https://github.com/golang/go/issues/36494
// - freebsd's backend is generally broken and asyncpreempt makes it even more so, see:
// https://github.com/go-delve/delve/issues/1754
// - on linux/arm64 asyncpreempt can sometimes restart a sequence of
// instructions, if the sequence happens to contain a breakpoint it will
// look like the breakpoint was hit twice when it was "logically" only
// executed once.
// See: https://go-review.googlesource.com/c/go/+/208126
DisableAsyncPreempt: runtime.GOOS == "windows" || runtime.GOOS == "freebsd" || (runtime.GOOS == "linux" && runtime.GOARCH == "arm64"),
StopReason: stopReason,
CanDump: runtime.GOOS == "linux"})
if err != nil {
return nil, err
}

@ -1335,6 +1335,10 @@ func TestFrameEvaluation(t *testing.T) {
g, err := proc.GetG(p.CurrentThread())
assertNoError(err, t, "GetG()")
frames, err := g.Stacktrace(40, 0)
t.Logf("Goroutine %d %#v", g.ID, g.Thread)
logStacktrace(t, p, frames)
for i := 0; i <= 3; i++ {
scope, err := proc.ConvertEvalScope(p, g.ID, i+1, 0)
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))

@ -276,6 +276,9 @@ func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
it.regs.FrameBase = it.frameBase(fn)
}
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, stackHi: it.stackhi, SystemStack: it.systemstack, lastpc: it.pc}
if r.Regs.Reg(it.regs.PCRegNum) == nil {
r.Regs.AddReg(it.regs.PCRegNum, op.DwarfRegisterFromUint64(it.pc))
}
r.Call = r.Current
if !it.top && r.Current.Fn != nil && it.pc != r.Current.Fn.Entry {
// if the return address is the entry point of the function that

@ -383,11 +383,11 @@ func MustHaveCgo(t *testing.T) {
func RegabiSupported() bool {
// Tracks regabiSupported variable in ParseGOEXPERIMENT internal/buildcfg/exp.go
switch {
case !goversion.VersionAfterOrEqual(runtime.Version(), 1, 17): // < 1.17
return false
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 18):
return runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64"
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17):
return runtime.GOARCH == "amd64" && (runtime.GOOS == "android" || runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows")
default: // >= 1.18
return runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64"
default:
return false
}
}

@ -2387,6 +2387,11 @@ func TestGlobalScopeAndVariables(t *testing.T) {
[]onBreakpoint{{
// Stop at line 36
execute: func() {
if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
client.StepInRequest(1)
client.ExpectStepInResponse(t)
client.ExpectStoppedEvent(t)
}
client.StackTraceRequest(1, 0, 20)
stack := client.ExpectStackTraceResponse(t)
checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3)
@ -2454,6 +2459,12 @@ func TestRegistersScopeAndVariables(t *testing.T) {
[]onBreakpoint{{
// Stop at line 36
execute: func() {
if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
client.StepInRequest(1)
client.ExpectStepInResponse(t)
client.ExpectStoppedEvent(t)
}
client.StackTraceRequest(1, 0, 20)
stack := client.ExpectStackTraceResponse(t)
checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3)

@ -331,7 +331,7 @@ func (p *iimporter) pkgAt(off uint64) *types.Package {
}
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
if t, ok := p.typCache[off]; ok && canReuse(base, t) {
return t
}
@ -343,12 +343,30 @@ func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
r.declReader.Reset(p.declData[off-predeclReserved:])
t := r.doType(base)
if base == nil || !isInterface(t) {
if canReuse(base, t) {
p.typCache[off] = t
}
return t
}
// canReuse reports whether the type rhs on the RHS of the declaration for def
// may be re-used.
//
// Specifically, if def is non-nil and rhs is an interface type with methods, it
// may not be re-used because we have a convention of setting the receiver type
// for interface methods to def.
func canReuse(def *types.Named, rhs types.Type) bool {
if def == nil {
return true
}
iface, _ := rhs.(*types.Interface)
if iface == nil {
return true
}
// Don't use iface.Empty() here as iface may not be complete.
return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
}
type importReader struct {
p *iimporter
declReader bytes.Reader

@ -16,5 +16,8 @@ func additionalPredeclared() []types.Type {
return []types.Type{
// comparable
types.Universe.Lookup("comparable").Type(),
// any
types.Universe.Lookup("any").Type(),
}
}

@ -13,6 +13,7 @@ package typeparams
import (
"go/ast"
"go/token"
"go/types"
)
// A IndexExprData holds data from both ast.IndexExpr and the new
@ -23,3 +24,9 @@ type IndexExprData struct {
Indices []ast.Expr // index expressions
Rbrack token.Pos // position of "]"
}
// IsTypeParam reports whether t is a type parameter.
func IsTypeParam(t types.Type) bool {
_, ok := t.(*TypeParam)
return ok
}

@ -16,92 +16,96 @@ import (
const debug = false
// NormalizeInterface returns the normal form of the interface iface, or nil if iface
// has an empty type set (i.e. there are no types that satisfy iface). If the
// resulting interface is non-nil, it will be identical to iface.
var ErrEmptyTypeSet = errors.New("empty type set")
// StructuralTerms returns a slice of terms representing the normalized
// structural type restrictions of a type parameter, if any.
//
// An error is returned if the interface type is invalid, or too complicated to
// reasonably normalize (for example, contains unions with more than a hundred
// terms).
// Structural type restrictions of a type parameter are created via
// non-interface types embedded in its constraint interface (directly, or via a
// chain of interface embeddings). For example, in the declaration
// type T[P interface{~int; m()}] int
// the structural restriction of the type parameter P is ~int.
//
// An interface is in normal form if and only if:
// - it has 0 or 1 embedded types.
// - its embedded type is either a types.Union or has a concrete
// (non-interface) underlying type
// - if the embedded type is a union, each term of the union has a concrete
// underlying type, and no terms may be removed without changing the type set
// of the interface
func NormalizeInterface(iface *types.Interface) (*types.Interface, error) {
var methods []*types.Func
for i := 0; i < iface.NumMethods(); i++ {
methods = append(methods, iface.Method(i))
// With interface embedding and unions, the specification of structural type
// restrictions may be arbitrarily complex. For example, consider the
// following:
//
// type A interface{ ~string|~[]byte }
//
// type B interface{ int|string }
//
// type C interface { ~string|~int }
//
// type T[P interface{ A|B; C }] int
//
// In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
// which when intersected with C (~string|~int) yields ~string|int.
//
// StructuralTerms computes these expansions and reductions, producing a
// "normalized" form of the embeddings. A structural restriction is normalized
// if it is a single union containing no interface terms, and is minimal in the
// sense that removing any term changes the set of types satisfying the
// constraint. It is left as a proof for the reader that, modulo sorting, there
// is exactly one such normalized form.
//
// Because the minimal representation always takes this form, StructuralTerms
// returns a slice of tilde terms corresponding to the terms of the union in
// the normalized structural restriction. An error is returned if the
// constraint interface is invalid, exceeds complexity bounds, or has an empty
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
//
// StructuralTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func StructuralTerms(tparam *TypeParam) ([]*Term, error) {
constraint := tparam.Constraint()
if constraint == nil {
return nil, fmt.Errorf("%s has nil constraint", tparam)
}
var embeddeds []types.Type
tset, err := computeTermSet(iface, make(map[types.Type]*termSet), 0)
iface, _ := constraint.Underlying().(*types.Interface)
if iface == nil {
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
}
return InterfaceTermSet(iface)
}
// InterfaceTermSet computes the normalized terms for a constraint interface,
// returning an error if the term set cannot be computed or is empty. In the
// latter case, the error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
return computeTermSet(iface)
}
// UnionTermSet computes the normalized terms for a union, returning an error
// if the term set cannot be computed or is empty. In the latter case, the
// error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func UnionTermSet(union *Union) ([]*Term, error) {
return computeTermSet(union)
}
func computeTermSet(typ types.Type) ([]*Term, error) {
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
}
switch {
case tset.terms.isEmpty():
// Special case: as documented
if tset.terms.isEmpty() {
return nil, ErrEmptyTypeSet
}
if tset.terms.isAll() {
return nil, nil
case tset.terms.isAll():
// No embeddeds.
case len(tset.terms) == 1:
if !tset.terms[0].tilde {
embeddeds = append(embeddeds, tset.terms[0].typ)
break
}
fallthrough
default:
var terms []*Term
for _, term := range tset.terms {
terms = append(terms, NewTerm(term.tilde, term.typ))
}
embeddeds = append(embeddeds, NewUnion(terms))
}
return types.NewInterfaceType(methods, embeddeds), nil
}
var ErrEmptyTypeSet = errors.New("empty type set")
// StructuralTerms returns the normalized structural type restrictions of a
// type, if any. For types that are not type parameters, it returns term slice
// containing a single non-tilde term holding the given type. For type
// parameters, it returns the normalized term list of the type parameter's
// constraint. See NormalizeInterface for more information on the normal form
// of a constraint interface.
//
// StructuralTerms returns an error if the structural term list cannot be
// computed. If the type set of typ is empty, it returns ErrEmptyTypeSet.
func StructuralTerms(typ types.Type) ([]*Term, error) {
switch typ := typ.(type) {
case *TypeParam:
iface, _ := typ.Constraint().(*types.Interface)
if iface == nil {
return nil, fmt.Errorf("constraint is %T, not *types.Interface", typ)
}
tset, err := computeTermSet(iface, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
}
if tset.terms.isEmpty() {
return nil, ErrEmptyTypeSet
}
if tset.terms.isAll() {
return nil, nil
}
var terms []*Term
for _, term := range tset.terms {
terms = append(terms, NewTerm(term.tilde, term.typ))
}
return terms, nil
default:
return []*Term{NewTerm(false, typ)}, nil
var terms []*Term
for _, term := range tset.terms {
terms = append(terms, NewTerm(term.tilde, term.typ))
}
return terms, nil
}
// A termSet holds the normalized set of terms for a given type.
@ -118,7 +122,7 @@ func indentf(depth int, format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
}
func computeTermSet(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
if t == nil {
panic("nil type")
}
@ -159,7 +163,7 @@ func computeTermSet(t types.Type, seen map[types.Type]*termSet, depth int) (res
if _, ok := embedded.Underlying().(*TypeParam); ok {
return nil, fmt.Errorf("invalid embedded type %T", embedded)
}
tset2, err := computeTermSet(embedded, seen, depth+1)
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
if err != nil {
return nil, err
}
@ -173,7 +177,7 @@ func computeTermSet(t types.Type, seen map[types.Type]*termSet, depth int) (res
var terms termlist
switch t.Type().Underlying().(type) {
case *types.Interface:
tset2, err := computeTermSet(t.Type(), seen, depth+1)
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
if err != nil {
return nil, err
}

@ -75,6 +75,7 @@ func ForFuncType(*ast.FuncType) *ast.FieldList {
// this Go version. Its methods panic on use.
type TypeParam struct{ types.Type }
func (*TypeParam) Index() int { unsupported(); return 0 }
func (*TypeParam) Constraint() types.Type { unsupported(); return nil }
func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil }

2
vendor/modules.txt vendored

@ -73,7 +73,7 @@ golang.org/x/sys/execabs
golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/tools v0.1.8-0.20211028023602-8de2a7fd1736
# golang.org/x/tools v0.1.8
## explicit
golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/gcimporter