mirror of https://github.com/golang/go.git
[release-branch.go1.21] go/types, types2: don't panic during interface completion
It should be possible for the importer to construct an invalid
interface, as would have been produced by type checking.
Updates #61737
Fixes #61743
Change-Id: I72e063f4f1a6205d273a623acce2ec08c34c3cc2
Reviewed-on: https://go-review.googlesource.com/c/go/+/515555
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
Reviewed-by: Olif Oftimis <oftimisolif@gmail.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit d2ee7821d3)
Reviewed-on: https://go-review.googlesource.com/c/go/+/515636
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
d91843ff67
commit
4e34f2e81d
|
|
@ -2070,6 +2070,29 @@ func TestIdenticalUnions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue61737(t *testing.T) {
|
||||||
|
// This test verifies that it is possible to construct invalid interfaces
|
||||||
|
// containing duplicate methods using the go/types API.
|
||||||
|
//
|
||||||
|
// It must be possible for importers to construct such invalid interfaces.
|
||||||
|
// Previously, this panicked.
|
||||||
|
|
||||||
|
sig1 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[Int])), nil, false)
|
||||||
|
sig2 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[String])), nil, false)
|
||||||
|
|
||||||
|
methods := []*Func{
|
||||||
|
NewFunc(nopos, nil, "M", sig1),
|
||||||
|
NewFunc(nopos, nil, "M", sig2),
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddedMethods := []*Func{
|
||||||
|
NewFunc(nopos, nil, "M", sig2),
|
||||||
|
}
|
||||||
|
embedded := NewInterfaceType(embeddedMethods, nil)
|
||||||
|
iface := NewInterfaceType(methods, []Type{embedded})
|
||||||
|
iface.NumMethods() // unlike go/types, there is no Complete() method, so we complete implicitly
|
||||||
|
}
|
||||||
|
|
||||||
func TestIssue15305(t *testing.T) {
|
func TestIssue15305(t *testing.T) {
|
||||||
const src = "package p; func f() int16; var _ = f(undef)"
|
const src = "package p; func f() int16; var _ = f(undef)"
|
||||||
f := mustParse(src)
|
f := mustParse(src)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package types2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"fmt"
|
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -212,7 +211,6 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||||
// we can get rid of the mpos map below and simply use the cloned method's
|
// we can get rid of the mpos map below and simply use the cloned method's
|
||||||
// position.
|
// position.
|
||||||
|
|
||||||
var todo []*Func
|
|
||||||
var seen objset
|
var seen objset
|
||||||
var allMethods []*Func
|
var allMethods []*Func
|
||||||
mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
|
mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
|
||||||
|
|
@ -222,36 +220,30 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||||
allMethods = append(allMethods, m)
|
allMethods = append(allMethods, m)
|
||||||
mpos[m] = pos
|
mpos[m] = pos
|
||||||
case explicit:
|
case explicit:
|
||||||
if check == nil {
|
if check != nil {
|
||||||
panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name))
|
var err error_
|
||||||
|
err.code = DuplicateDecl
|
||||||
|
err.errorf(pos, "duplicate method %s", m.name)
|
||||||
|
err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
|
||||||
|
check.report(&err)
|
||||||
}
|
}
|
||||||
// check != nil
|
|
||||||
var err error_
|
|
||||||
err.code = DuplicateDecl
|
|
||||||
err.errorf(pos, "duplicate method %s", m.name)
|
|
||||||
err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
|
|
||||||
check.report(&err)
|
|
||||||
default:
|
default:
|
||||||
// We have a duplicate method name in an embedded (not explicitly declared) method.
|
// We have a duplicate method name in an embedded (not explicitly declared) method.
|
||||||
// Check method signatures after all types are computed (go.dev/issue/33656).
|
// Check method signatures after all types are computed (go.dev/issue/33656).
|
||||||
// If we're pre-go1.14 (overlapping embeddings are not permitted), report that
|
// If we're pre-go1.14 (overlapping embeddings are not permitted), report that
|
||||||
// error here as well (even though we could do it eagerly) because it's the same
|
// error here as well (even though we could do it eagerly) because it's the same
|
||||||
// error message.
|
// error message.
|
||||||
if check == nil {
|
if check != nil {
|
||||||
// check method signatures after all locally embedded interfaces are computed
|
check.later(func() {
|
||||||
todo = append(todo, m, other.(*Func))
|
if !check.allowVersion(m.pkg, pos, go1_14) || !Identical(m.typ, other.Type()) {
|
||||||
break
|
var err error_
|
||||||
|
err.code = DuplicateDecl
|
||||||
|
err.errorf(pos, "duplicate method %s", m.name)
|
||||||
|
err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
|
||||||
|
check.report(&err)
|
||||||
|
}
|
||||||
|
}).describef(pos, "duplicate method check for %s", m.name)
|
||||||
}
|
}
|
||||||
// check != nil
|
|
||||||
check.later(func() {
|
|
||||||
if !check.allowVersion(m.pkg, pos, go1_14) || !Identical(m.typ, other.Type()) {
|
|
||||||
var err error_
|
|
||||||
err.code = DuplicateDecl
|
|
||||||
err.errorf(pos, "duplicate method %s", m.name)
|
|
||||||
err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
|
|
||||||
check.report(&err)
|
|
||||||
}
|
|
||||||
}).describef(pos, "duplicate method check for %s", m.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,15 +306,6 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||||
}
|
}
|
||||||
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
||||||
|
|
||||||
// process todo's (this only happens if check == nil)
|
|
||||||
for i := 0; i < len(todo); i += 2 {
|
|
||||||
m := todo[i]
|
|
||||||
other := todo[i+1]
|
|
||||||
if !Identical(m.typ, other.typ) {
|
|
||||||
panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ityp.tset.comparable = allComparable
|
ityp.tset.comparable = allComparable
|
||||||
if len(allMethods) != 0 {
|
if len(allMethods) != 0 {
|
||||||
sortMethods(allMethods)
|
sortMethods(allMethods)
|
||||||
|
|
|
||||||
|
|
@ -2071,6 +2071,29 @@ func TestIdenticalUnions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue61737(t *testing.T) {
|
||||||
|
// This test verifies that it is possible to construct invalid interfaces
|
||||||
|
// containing duplicate methods using the go/types API.
|
||||||
|
//
|
||||||
|
// It must be possible for importers to construct such invalid interfaces.
|
||||||
|
// Previously, this panicked.
|
||||||
|
|
||||||
|
sig1 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[Int])), nil, false)
|
||||||
|
sig2 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[String])), nil, false)
|
||||||
|
|
||||||
|
methods := []*Func{
|
||||||
|
NewFunc(nopos, nil, "M", sig1),
|
||||||
|
NewFunc(nopos, nil, "M", sig2),
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddedMethods := []*Func{
|
||||||
|
NewFunc(nopos, nil, "M", sig2),
|
||||||
|
}
|
||||||
|
embedded := NewInterfaceType(embeddedMethods, nil)
|
||||||
|
iface := NewInterfaceType(methods, []Type{embedded})
|
||||||
|
iface.Complete()
|
||||||
|
}
|
||||||
|
|
||||||
func TestIssue15305(t *testing.T) {
|
func TestIssue15305(t *testing.T) {
|
||||||
const src = "package p; func f() int16; var _ = f(undef)"
|
const src = "package p; func f() int16; var _ = f(undef)"
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -216,7 +215,6 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
// we can get rid of the mpos map below and simply use the cloned method's
|
// we can get rid of the mpos map below and simply use the cloned method's
|
||||||
// position.
|
// position.
|
||||||
|
|
||||||
var todo []*Func
|
|
||||||
var seen objset
|
var seen objset
|
||||||
var allMethods []*Func
|
var allMethods []*Func
|
||||||
mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
|
mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
|
||||||
|
|
@ -226,30 +224,24 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
allMethods = append(allMethods, m)
|
allMethods = append(allMethods, m)
|
||||||
mpos[m] = pos
|
mpos[m] = pos
|
||||||
case explicit:
|
case explicit:
|
||||||
if check == nil {
|
if check != nil {
|
||||||
panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
|
check.errorf(atPos(pos), DuplicateDecl, "duplicate method %s", m.name)
|
||||||
|
check.errorf(atPos(mpos[other.(*Func)]), DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
|
||||||
}
|
}
|
||||||
// check != nil
|
|
||||||
check.errorf(atPos(pos), DuplicateDecl, "duplicate method %s", m.name)
|
|
||||||
check.errorf(atPos(mpos[other.(*Func)]), DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
|
|
||||||
default:
|
default:
|
||||||
// We have a duplicate method name in an embedded (not explicitly declared) method.
|
// We have a duplicate method name in an embedded (not explicitly declared) method.
|
||||||
// Check method signatures after all types are computed (go.dev/issue/33656).
|
// Check method signatures after all types are computed (go.dev/issue/33656).
|
||||||
// If we're pre-go1.14 (overlapping embeddings are not permitted), report that
|
// If we're pre-go1.14 (overlapping embeddings are not permitted), report that
|
||||||
// error here as well (even though we could do it eagerly) because it's the same
|
// error here as well (even though we could do it eagerly) because it's the same
|
||||||
// error message.
|
// error message.
|
||||||
if check == nil {
|
if check != nil {
|
||||||
// check method signatures after all locally embedded interfaces are computed
|
check.later(func() {
|
||||||
todo = append(todo, m, other.(*Func))
|
if !check.allowVersion(m.pkg, atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
|
||||||
break
|
check.errorf(atPos(pos), DuplicateDecl, "duplicate method %s", m.name)
|
||||||
|
check.errorf(atPos(mpos[other.(*Func)]), DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
|
||||||
|
}
|
||||||
|
}).describef(atPos(pos), "duplicate method check for %s", m.name)
|
||||||
}
|
}
|
||||||
// check != nil
|
|
||||||
check.later(func() {
|
|
||||||
if !check.allowVersion(m.pkg, atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
|
|
||||||
check.errorf(atPos(pos), DuplicateDecl, "duplicate method %s", m.name)
|
|
||||||
check.errorf(atPos(mpos[other.(*Func)]), DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
|
|
||||||
}
|
|
||||||
}).describef(atPos(pos), "duplicate method check for %s", m.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,15 +304,6 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
}
|
}
|
||||||
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
||||||
|
|
||||||
// process todo's (this only happens if check == nil)
|
|
||||||
for i := 0; i < len(todo); i += 2 {
|
|
||||||
m := todo[i]
|
|
||||||
other := todo[i+1]
|
|
||||||
if !Identical(m.typ, other.typ) {
|
|
||||||
panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ityp.tset.comparable = allComparable
|
ityp.tset.comparable = allComparable
|
||||||
if len(allMethods) != 0 {
|
if len(allMethods) != 0 {
|
||||||
sortMethods(allMethods)
|
sortMethods(allMethods)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue