[dev.boringcrypto] all: merge master into dev.boringcrypto

Change-Id: If6b68df0c90464566e68de6807d15f4b8bec6219
This commit is contained in:
Chressie Himpel 2022-02-08 10:31:51 +01:00
commit 74d25c624c
122 changed files with 2257 additions and 2667 deletions

View File

@ -1,12 +1,6 @@
pkg bufio, method (*Writer) AvailableBuffer() []uint8
pkg bufio, method (ReadWriter) AvailableBuffer() []uint8
pkg bytes, func Cut([]uint8, []uint8) ([]uint8, []uint8, bool)
pkg constraints, type Complex interface {}
pkg constraints, type Float interface {}
pkg constraints, type Integer interface {}
pkg constraints, type Ordered interface {}
pkg constraints, type Signed interface {}
pkg constraints, type Unsigned interface {}
pkg crypto/tls, method (*Conn) NetConn() net.Conn
pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error)
pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error)

View File

@ -90,6 +90,39 @@ Do not send CLs removing the interior tags from such phrases.
</li>
</ul>
<p>
There are three experimental packages using generics that may be
useful.
These packages are in x/exp repository; their API is not covered by
the Go 1 guarantee and may change as we gain more experience with
generics.
<dl>
<dt><a href="https://pkg.go.dev/golang.org/x/exp/constraints"><code>golang.org/x/exp/constraints</code></a></dt>
<dd>
<p>
Constraints that are useful for generic code, such as
<a href="https://pkg.go.dev/golang.org/x/exp/constraints#Ordered"><code>constraints.Ordered</code></a>.
</p>
</dd>
<dt><a href="https://pkg.go.dev/golang.org/x/exp/slices"><code>golang.org/x/exp/slices</code></a></dt>
<dd>
<p>
A collection of generic functions that operate on slices of
any element type.
</p>
</dd>
<dt><a href="https://pkg.go.dev/golang.org/x/exp/maps"><code>golang.org/x/exp/maps</code></a></dt>
<dd>
<p>
A collection of generic functions that operate on maps of
any key or element type.
</p>
</dd>
</dl>
</p>
<p>
The current generics implementation has the following limitations:
<ul>
@ -460,6 +493,14 @@ Do not send CLs removing the interior tags from such phrases.
<h2 id="linker">Linker</h2>
<p>
The linker emits <a href="https://tailscale.com/blog/go-linker/">far fewer relocations</a>.
As a result, most codebases will link faster, require less memory to link,
and generate smaller binaries.
Tools that process Go binaries should use Go 1.18's <code>debug/gosym</code> package
to transparently handle both old and new binaries.
</p>
<p><!-- CL 298610 -->
The new linker <code>-asan</code> option supports the
new <code>go</code> command <code>-asan</code> option.
@ -467,14 +508,6 @@ Do not send CLs removing the interior tags from such phrases.
<h2 id="library">Core library</h2>
<h3 id="constraints">New <code>constraints</code> package</h3>
<p><!-- CL 349709 -->
The new <a href="/pkg/constraints/"><code>constraints</code></a> package
defines a set of useful constraints that can be used with type parameters of
generic functions.
</p>
<h3 id="debug/buildinfo">New <code>debug/buildinfo</code> package</h3>
<p><!-- golang.org/issue/39301 -->
@ -670,6 +703,46 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl><!-- crypto/tls -->
<dl id="crypto/x509"><dt><a href="/pkg/crypto/x509">crypto/x509</a></dt>
<dd>
<p><!-- CL 353132, CL 353403 -->
<a href="/pkg/crypto/x509/#Certificate.Verify"><code>Certificate.Verify</code></a>
now uses platform APIs to verify certificate validity on macOS and iOS when it
is called with a nil
<a href="/pkg/crypto/x509/#VerifyOpts.Roots"><code>VerifyOpts.Roots</code></a>
or when using the root pool returned from
<a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>.
</p>
<p><!-- CL 353589 -->
<a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
is now available on Windows.
</p>
<p>
On Windows, macOS, and iOS, when a
<a href="/pkg/crypto/x509/#CertPool"><code>CertPool</code></a> returned by
<a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
has additional certificates added to it,
<a href="/pkg/crypto/x509/#Certificate.Verify"><code>Certificate.Verify</code></a>
will do two verifications: one using the platform verifier APIs and the
system roots, and one using the Go verifier and the additional roots.
Chains returned by the platform verifier APIs will be prioritized.
</p>
<p>
<a href="/pkg/crypto/x509/#CertPool.Subjects"><code>CertPool.Subjects</code></a>
is deprecated. On Windows, macOS, and iOS the
<a href="/pkg/crypto/x509/#CertPool"><code>CertPool</code></a> returned by
<a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
will return a pool which does not include system roots in the slice
returned by <code>Subjects</code>, as a static list can't appropriately
represent the platform policies and might not be available at all from the
platform APIs.
</p>
</dd>
</dl>
<dl id="debug/dwarf"><dt><a href="/pkg/debug/dwarf/">debug/dwarf</a></dt>
<dd>
<p><!-- CL 380714 -->

View File

@ -201,6 +201,10 @@ func cCompilerCmd(t *testing.T) []string {
if !lastSpace {
cc = append(cc, s[start:])
}
// Force reallocation (and avoid aliasing bugs) for tests that append to cc.
cc = cc[:len(cc):len(cc)]
return cc
}

View File

@ -367,6 +367,11 @@ void init() {
// Cgo incorrectly computed the alignment of structs
// with no Go accessible fields as 0, and then panicked on
// modulo-by-zero computations.
// issue 50987
// disable arm64 GCC warnings
#cgo CFLAGS: -Wno-psabi -Wno-unknown-warning-option
typedef struct {
} foo;

View File

@ -11,7 +11,6 @@ import (
"flag"
"fmt"
"io"
"io/fs"
"log"
"os"
"os/exec"
@ -141,6 +140,9 @@ func testMain(m *testing.M) int {
libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive")
cc = append(cc, "-I", libgodir)
// Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
cc = cc[:len(cc):len(cc)]
if GOOS == "windows" {
exeSuffix = ".exe"
}
@ -248,29 +250,6 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
// checkIsExecutable verifies that exe exists and has execute permission.
//
// (https://golang.org/issue/49693 notes failures with "no such file or
// directory", so we want to double-check that the executable actually exists
// immediately after we build it in order to better understand that failure
// mode.)
func checkIsExecutable(t *testing.T, exe string) {
t.Helper()
fi, err := os.Stat(exe)
if err != nil {
t.Fatal(err)
}
if runtime.GOOS == "windows" {
// os.File doesn't check the "execute" permission on Windows files
// and as a result doesn't set that bit in a file's permissions.
// Assume that if the file exists it is “executable enough”.
return
}
if fi.Mode()&0111 == 0 {
t.Fatalf("%s is not executable: %0o", exe, fi.Mode()&fs.ModePerm)
}
}
// checkLineComments checks that the export header generated by
// -buildmode=c-archive doesn't have any absolute paths in the #line
// comments. We don't want those paths because they are unhelpful for
@ -964,7 +943,6 @@ func TestSIGPROF(t *testing.T) {
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp6"+exeSuffix)
argv := cmdToRun("./testp6")
cmd = exec.Command(argv[0], argv[1:]...)
@ -1113,7 +1091,6 @@ func TestManyCalls(t *testing.T) {
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp7"+exeSuffix)
argv := cmdToRun("./testp7")
cmd = exec.Command(argv[0], argv[1:]...)
@ -1170,7 +1147,6 @@ func TestPreemption(t *testing.T) {
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp8"+exeSuffix)
argv := cmdToRun("./testp8")
cmd = exec.Command(argv[0], argv[1:]...)

View File

@ -117,6 +117,9 @@ func testMain(m *testing.M) int {
}
cc = append(cc, "-I", filepath.Join("pkg", libgodir))
// Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
cc = cc[:len(cc):len(cc)]
if GOOS == "windows" {
exeSuffix = ".exe"
}

View File

@ -1480,6 +1480,7 @@ func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
s.SetUnderlying(u)
s.SetIsShape(true)
s.SetHasShape(true)
types.CalcSize(s)
name.SetType(s)
name.SetTypecheck(1)
submap[u] = s

View File

@ -2369,3 +2369,61 @@ type Bad Bad // invalid type
}
}
}
func TestMissingMethodAlternative(t *testing.T) {
const src = `
package p
type T interface {
m()
}
type V0 struct{}
func (V0) m() {}
type V1 struct{}
type V2 struct{}
func (V2) m() int
type V3 struct{}
func (*V3) m()
type V4 struct{}
func (V4) M()
`
pkg, err := pkgFor("p.go", src, nil)
if err != nil {
t.Fatal(err)
}
T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
lookup := func(name string) (*Func, bool) {
return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true)
}
// V0 has method m with correct signature. Should not report wrongType.
method, wrongType := lookup("V0")
if method != nil || wrongType {
t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType)
}
checkMissingMethod := func(tname string, reportWrongType bool) {
method, wrongType := lookup(tname)
if method == nil || method.Name() != "m" || wrongType != reportWrongType {
t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType)
}
}
// V1 has no method m. Should not report wrongType.
checkMissingMethod("V1", false)
// V2 has method m with wrong signature type (ignoring receiver). Should report wrongType.
checkMissingMethod("V2", true)
// V3 has no method m but it exists on *V3. Should report wrongType.
checkMissingMethod("V3", true)
// V4 has no method m but has M. Should not report wrongType.
checkMissingMethod("V4", false)
}

View File

@ -142,9 +142,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// cap(x)
// len(x)
mode := invalid
var typ Type
var val constant.Value
switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
switch t := arrayPtrDeref(under(x.typ)).(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
@ -201,17 +200,19 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
}
if mode == invalid && typ != Typ[Invalid] {
if mode == invalid && under(x.typ) != Typ[Invalid] {
check.errorf(x, invalidArg+"%s for %s", x, bin.name)
return
}
// record the signature before changing x.typ
if check.Types != nil && mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ))
}
x.mode = mode
x.typ = Typ[Int]
x.val = val
if check.Types != nil && mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
}
case _Close:
// close(c)

View File

@ -28,6 +28,8 @@ var builtinCalls = []struct {
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
{"cap", `type S []byte; var s S; _ = cap(s)`, `func(p.S) int`},
{"cap", `var s P; _ = cap(s)`, `func(P) int`},
{"len", `_ = len("foo")`, `invalid type`}, // constant
{"len", `var s string; _ = len(s)`, `func(string) int`},
@ -36,6 +38,8 @@ var builtinCalls = []struct {
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
{"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`},
{"len", `var s P; _ = len(s)`, `func(P) int`},
{"close", `var c chan int; close(c)`, `func(chan int)`},
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
@ -159,7 +163,7 @@ func parseGenericSrc(path, src string) (*syntax.File, error) {
}
func testBuiltinSignature(t *testing.T, name, src0, want string) {
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0)
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P ~[]byte]() { %s }`, src0)
f, err := parseGenericSrc("", src)
if err != nil {
t.Errorf("%s: %s", src0, err)

View File

@ -167,8 +167,13 @@ func (check *Checker) markImports(pkg *Package) {
}
}
// check may be nil.
func (check *Checker) sprintf(format string, args ...interface{}) string {
return sprintf(check.qualifier, false, format, args...)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
return sprintf(qf, false, format, args...)
}
func (check *Checker) report(err *error_) {

View File

@ -770,52 +770,82 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return target, nil, 0
}
func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
// If switchCase is true, the operator op is ignored.
func (check *Checker) comparison(x, y *operand, op syntax.Operator, switchCase bool) {
if switchCase {
op = syntax.Eql
}
errOp := x // operand for which error is reported, if any
cause := "" // specific error cause, if any
// spec: "In any comparison, the first operand must be assignable
// to the type of the second operand, or vice versa."
err := ""
xok, _ := x.assignableTo(check, y.typ, nil)
yok, _ := y.assignableTo(check, x.typ, nil)
if xok || yok {
equality := false
defined := false
switch op {
case syntax.Eql, syntax.Neq:
// spec: "The equality operators == and != apply to operands that are comparable."
equality = true
defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
defined = allOrdered(x.typ) && allOrdered(y.typ)
default:
unreachable()
ok, _ := x.assignableTo(check, y.typ, nil)
if !ok {
ok, _ = y.assignableTo(check, x.typ, nil)
}
if !ok {
// Report the error on the 2nd operand since we only
// know after seeing the 2nd operand whether we have
// a type mismatch.
errOp = y
// For now, if we're not running the compiler, use the
// position of x to minimize changes to existing tests.
if !check.conf.CompilerErrorMessages {
errOp = x
}
if !defined {
if equality && (isTypeParam(x.typ) || isTypeParam(y.typ)) {
typ := x.typ
if isTypeParam(y.typ) {
typ = y.typ
}
err = check.sprintf("%s is not comparable", typ)
} else {
typ := x.typ
if x.isNil() {
typ = y.typ
}
err = check.sprintf("operator %s not defined on %s", op, typ)
cause = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
goto Error
}
// check if comparison is defined for operands
switch op {
case syntax.Eql, syntax.Neq:
// spec: "The equality operators == and != apply to operands that are comparable."
switch {
case x.isNil() || y.isNil():
// Comparison against nil requires that the other operand type has nil.
typ := x.typ
if x.isNil() {
typ = y.typ
}
if !hasNil(typ) {
// This case should only be possible for "nil == nil".
// Report the error on the 2nd operand since we only
// know after seeing the 2nd operand whether we have
// an invalid comparison.
errOp = y
goto Error
}
case !Comparable(x.typ):
errOp = x
cause = check.incomparableCause(x.typ)
goto Error
case !Comparable(y.typ):
errOp = y
cause = check.incomparableCause(y.typ)
goto Error
}
} else {
err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
}
if err != "" {
// TODO(gri) better error message for cases where one can only compare against nil
check.errorf(x, invalidOp+"cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
x.mode = invalid
return
case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
switch {
case !allOrdered(x.typ):
errOp = x
goto Error
case !allOrdered(y.typ):
errOp = y
goto Error
}
default:
unreachable()
}
// comparison is ok
if x.mode == constant_ && y.mode == constant_ {
x.val = constant.MakeBool(constant.Compare(x.val, op2tok[op], y.val))
// The operands are never materialized; no need to update
@ -833,6 +863,73 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
// spec: "Comparison operators compare two operands and yield
// an untyped boolean value."
x.typ = Typ[UntypedBool]
return
Error:
// We have an offending operand errOp and possibly an error cause.
if cause == "" {
if isTypeParam(x.typ) || isTypeParam(y.typ) {
// TODO(gri) should report the specific type causing the problem, if any
if !isTypeParam(x.typ) {
errOp = y
}
cause = check.sprintf("type parameter %s is not comparable with %s", errOp.typ, op)
} else {
cause = check.sprintf("operator %s not defined on %s", op, check.kindString(errOp.typ)) // catch-all
}
}
if switchCase {
check.errorf(x, "invalid case %s in switch on %s (%s)", x.expr, y.expr, cause) // error position always at 1st operand
} else {
if check.conf.CompilerErrorMessages {
check.errorf(errOp, invalidOp+"%s %s %s (%s)", x.expr, op, y.expr, cause)
} else {
check.errorf(errOp, invalidOp+"cannot compare %s %s %s (%s)", x.expr, op, y.expr, cause)
}
}
x.mode = invalid
}
// incomparableCause returns a more specific cause why typ is not comparable.
// If there is no more specific cause, the result is "".
func (check *Checker) incomparableCause(typ Type) string {
switch under(typ).(type) {
case *Slice, *Signature, *Map:
return check.kindString(typ) + " can only be compared to nil"
}
// see if we can extract a more specific error
var cause string
comparable(typ, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
}
// kindString returns the type kind as a string.
func (check *Checker) kindString(typ Type) string {
switch under(typ).(type) {
case *Array:
return "array"
case *Slice:
return "slice"
case *Struct:
return "struct"
case *Pointer:
return "pointer"
case *Signature:
return "func"
case *Interface:
if isTypeParam(typ) {
return check.sprintf("type parameter %s", typ)
}
return "interface"
case *Map:
return "map"
case *Chan:
return "chan"
default:
return check.sprintf("%s", typ) // catch-all
}
}
// If e != nil, it must be the shift expression; it may be nil for non-constant shifts.
@ -1034,7 +1131,7 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
}
if isComparison(op) {
check.comparison(x, &y, op)
check.comparison(x, &y, op, false)
return
}
@ -1481,8 +1578,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
check.errorf(x, invalidOp+"cannot use type assertion on type parameter value %s", x)
goto Error
}
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
if _, ok := under(x.typ).(*Interface); !ok {
check.errorf(x, invalidOp+"%s is not an interface", x)
goto Error
}
@ -1495,7 +1591,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
if T == Typ[Invalid] {
goto Error
}
check.typeAssertion(e, x, xtyp, T, false)
check.typeAssertion(e, x, T, false)
x.mode = commaok
x.typ = T
@ -1636,28 +1732,21 @@ func keyVal(x constant.Value) interface{} {
return x
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
func (check *Checker) typeAssertion(e syntax.Expr, x *operand, xtyp *Interface, T Type, typeSwitch bool) {
method, wrongType := check.assertableTo(xtyp, T)
// typeAssertion checks x.(T). The type of x must be an interface.
func (check *Checker) typeAssertion(e syntax.Expr, x *operand, T Type, typeSwitch bool) {
method, alt := check.assertableTo(under(x.typ).(*Interface), T)
if method == nil {
return // success
}
cause := check.missingMethodReason(T, x.typ, method, alt)
if typeSwitch {
check.errorf(e, "impossible type switch case: %s\n\t%s cannot have dynamic type %s %s", e, x, T, cause)
return
}
var err error_
var msg string
if typeSwitch {
err.errorf(e.Pos(), "impossible type switch case: %s", e)
msg = check.sprintf("%s cannot have dynamic type %s %s", x, T,
check.missingMethodReason(T, x.typ, method, wrongType))
} else {
err.errorf(e.Pos(), "impossible type assertion: %s", e)
msg = check.sprintf("%s does not implement %s %s", T, x.typ,
check.missingMethodReason(T, x.typ, method, wrongType))
}
err.errorf(nopos, msg)
check.report(&err)
check.errorf(e, "impossible type assertion: %s\n\t%s does not implement %s %s", e, T, x.typ, cause)
}
// expr typechecks expression e and initializes x with the expression value.

View File

@ -179,7 +179,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
if arg.mode == invalid {
// An error was reported earlier. Ignore this targ
// and continue, we may still be able to infer all
// targs resulting in fewer follon-on errors.
// targs resulting in fewer follow-on errors.
continue
}
if targ := arg.typ; isTyped(targ) {
@ -190,7 +190,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
errorf("type", par.typ, targ, arg)
return nil
}
} else {
} else if _, ok := par.typ.(*TypeParam); ok {
// Since default types are all basic (i.e., non-composite) types, an
// untyped argument will never match a composite parameter type; the
// only parameter type it can possibly match against is a *TypeParam.
// Thus, for untyped arguments we only need to look at parameter types
// that are single type parameters.
indices = append(indices, i)
}
}
@ -219,20 +224,17 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// Some generic parameters with untyped arguments may have been given
// a type by now, we can ignore them.
for _, i := range indices {
par := params.At(i)
// Since untyped types are all basic (i.e., non-composite) types, an
// untyped argument will never match a composite parameter type; the
// only parameter type it can possibly match against is a *TypeParam.
// Thus, only consider untyped arguments for generic parameters that
// are not of composite types and which don't have a type inferred yet.
if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil {
tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of indices
// Only consider untyped arguments for which the corresponding type
// parameter doesn't have an inferred type yet.
if targs[tpar.index] == nil {
arg := args[i]
targ := Default(arg.typ)
// The default type for an untyped nil is untyped nil. We must not
// infer an untyped nil type as type parameter type. Ignore untyped
// nil by making sure all default argument types are typed.
if isTyped(targ) && !u.unify(par.typ, targ) {
errorf("default type", par.typ, targ, arg)
if isTyped(targ) && !u.unify(tpar, targ) {
errorf("default type", tpar, targ, arg)
return nil
}
}

View File

@ -135,6 +135,8 @@ func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool
func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type) (int, error) {
smap := makeSubstMap(tparams, targs)
for i, tpar := range tparams {
// Ensure that we have a (possibly implicit) interface as type bound (issue #51048).
tpar.iface()
// The type parameter bound is parameterized with the same type parameters
// as the instantiated type; before we can use it for bounds checking we
// need to instantiate it with the type arguments with which we instantiated
@ -160,21 +162,17 @@ func (check *Checker) implements(V, T Type) error {
return nil // avoid follow-on errors (see issue #49541 for an example)
}
var qf Qualifier
if check != nil {
qf = check.qualifier
}
errorf := func(format string, args ...interface{}) error {
return errors.New(sprintf(qf, false, format, args...))
return errors.New(check.sprintf(format, args...))
}
Ti, _ := Tu.(*Interface)
if Ti == nil {
var cause string
if isInterfacePtr(Tu) {
cause = sprintf(qf, false, "type %s is pointer to interface, not interface", T)
cause = check.sprintf("type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(qf, false, "%s is not an interface", T)
cause = check.sprintf("%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
@ -199,23 +197,8 @@ func (check *Checker) implements(V, T Type) error {
}
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
if check != nil && check.conf.CompilerErrorMessages {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
var cause string
if wrong != nil {
if Identical(m.typ, wrong.typ) {
cause = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
} else {
cause = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrong.typ, m.typ)
}
} else {
cause = "missing method " + m.Name()
}
return errorf("%s does not implement %s: %s", V, T, cause)
}
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
// If T is comparable, V must be comparable.

View File

@ -7,6 +7,7 @@
package types2
import (
"bytes"
"strings"
)
@ -55,7 +56,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// not have found it for T (see also issue 8590).
if t, _ := T.(*Named); t != nil {
if p, _ := t.Underlying().(*Pointer); p != nil {
obj, index, indirect = lookupFieldOrMethod(p, false, false, pkg, name)
obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name, false)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
@ -63,7 +64,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
}
}
obj, index, indirect = lookupFieldOrMethod(T, addressable, false, pkg, name)
obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name, false)
// If we didn't find anything and if we have a type parameter with a structural constraint,
// see if there is a matching field (but not a method, those need to be declared explicitly
@ -71,7 +72,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// are ok here because only fields are accepted as results.
if obj == nil && isTypeParam(T) {
if t := structuralType(T); t != nil {
obj, index, indirect = lookupFieldOrMethod(t, addressable, false, pkg, name)
obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name, false)
if _, ok := obj.(*Var); !ok {
obj, index, indirect = nil, nil, false // accept fields (variables) only
}
@ -86,11 +87,11 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// indirectly via different packages.)
// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
// If checkFold is true, the lookup for methods will include looking for any method
// If foldCase is true, the lookup for methods will include looking for any method
// which case-folds to the same as 'name' (used for giving helpful error messages).
//
// The resulting object may not be fully type-checked.
func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
if name == "_" {
@ -144,7 +145,7 @@ func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name
// look for a matching attached method
named.resolve(nil)
if i, m := named.lookupMethodFold(pkg, name, checkFold); m != nil {
if i, m := named.lookupMethod(pkg, name, foldCase); m != nil {
// potential match
// caution: method may not have a proper signature yet
index = concat(e.index, i)
@ -191,7 +192,7 @@ func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name
case *Interface:
// look for a matching method (interface may be a type parameter)
if i, m := lookupMethodFold(t.typeSet().methods, pkg, name, checkFold); m != nil {
if i, m := t.typeSet().LookupMethod(pkg, name, foldCase); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
@ -281,54 +282,38 @@ func lookupType(m map[Type]int, typ Type) (int, bool) {
// x is of interface type V).
//
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
m, typ := (*Checker)(nil).missingMethod(V, T, static)
return m, typ != nil
m, alt := (*Checker)(nil).missingMethod(V, T, static)
// Only report a wrong type if the alternative method has the same name as m.
return m, alt != nil && alt.name == m.name // alt != nil implies m != nil
}
// If we accept type parameters for methods, (at least) the code
// guarded with this constant will need to be adjusted when such
// methods are used (not just parsed).
const acceptMethodTypeParams = false
// missingMethod is like MissingMethod but accepts a *Checker as
// receiver and an addressable flag.
// The receiver may be nil if missingMethod is invoked through
// an exported API call (such as MissingMethod), i.e., when all
// methods have been type-checked.
// If the type has the correctly named method, but with the wrong
// signature, the existing method is returned as well.
// To improve error messages, also report the wrong signature
// when the method exists on *V instead of V.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
// fast path for common case
if T.Empty() {
// missingMethod is like MissingMethod but accepts a *Checker as receiver.
// The receiver may be nil if missingMethod is invoked through an exported
// API call (such as MissingMethod), i.e., when all methods have been type-
// checked.
//
// If a method is missing on T but is found on *T, or if a method is found
// on T when looked up with case-folding, this alternative method is returned
// as the second result.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) {
if T.NumMethods() == 0 {
return
}
if ityp, _ := under(V).(*Interface); ityp != nil {
// TODO(gri) the methods are sorted - could do this more efficiently
// V is an interface
if u, _ := under(V).(*Interface); u != nil {
tset := u.typeSet()
for _, m := range T.typeSet().methods {
_, f := ityp.typeSet().LookupMethod(m.pkg, m.name)
_, f := tset.LookupMethod(m.pkg, m.name, false)
if f == nil {
if !static {
continue
}
// We don't do any case-fold check if V is an interface.
return m, f
return m, nil
}
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
panic("method with type parameters")
}
if !Identical(ftyp, mtyp) {
if !Identical(f.typ, m.typ) {
return m, f
}
}
@ -336,31 +321,22 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
// A concrete type implements T if it implements all methods of T.
// V is not an interface
for _, m := range T.typeSet().methods {
// TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)?
obj, _, _ := lookupFieldOrMethod(V, false, false, m.pkg, m.name)
obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name, false)
// Check if *V implements this method of T.
if obj == nil {
ptr := NewPointer(V)
obj, _, _ = lookupFieldOrMethod(ptr, false, false, m.pkg, m.name)
// check if m is on *V, or on V with case-folding
found := obj != nil
if !found {
// TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument?
obj, _, _ = lookupFieldOrMethod(NewPointer(V), false, m.pkg, m.name, false)
if obj == nil {
// If we didn't find the exact method (even with pointer
// receiver), look to see if there is a method that
// matches m.name with case-folding.
obj, _, _ = lookupFieldOrMethod(V, false, true, m.pkg, m.name)
}
if obj != nil {
// methods may not have a fully set up signature yet
if check != nil {
check.objDecl(obj, nil)
}
return m, obj.(*Func)
obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true /* fold case */)
}
}
// we must have a method (not a field of matching function type)
// we must have a method (not a struct field)
f, _ := obj.(*Func)
if f == nil {
return m, nil
@ -371,17 +347,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
check.objDecl(f, nil)
}
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
panic("method with type parameters")
}
if !Identical(ftyp, mtyp) {
if !found || !Identical(f.typ, m.typ) {
return m, f
}
}
@ -391,48 +357,41 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// missingMethodReason returns a string giving the detailed reason for a missing method m,
// where m is missing from V, but required by T. It puts the reason in parentheses,
// and may include more have/want info after that. If non-nil, wrongType is a relevant
// and may include more have/want info after that. If non-nil, alt is a relevant
// method that matches in some way. It may have the correct name, but wrong type, or
// it may have a pointer receiver, or it may have the correct name except wrong case.
func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string {
var r string
// check may be nil.
func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string {
var mname string
if check.conf.CompilerErrorMessages {
if check != nil && check.conf.CompilerErrorMessages {
mname = m.Name() + " method"
} else {
mname = "method " + m.Name()
}
if wrongType != nil {
if m.Name() != wrongType.Name() {
r = check.sprintf("(missing %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
} else if Identical(m.typ, wrongType.typ) {
r = check.sprintf("(%s has pointer receiver)", mname)
} else {
if check.conf.CompilerErrorMessages {
r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
} else {
r = check.sprintf("(wrong type for %s)\n\thave %s\n\twant %s",
mname, wrongType.typ, m.typ)
}
if alt != nil {
if m.Name() != alt.Name() {
return check.sprintf("(missing %s)\n\t\thave %s\n\t\twant %s",
mname, check.funcString(alt), check.funcString(m))
}
// This is a hack to print the function type without the leading
// 'func' keyword in the have/want printouts. We could change to have
// an extra formatting option for types2.Type that doesn't print out
// 'func'.
r = strings.Replace(r, "^^func", "", -1)
} else if IsInterface(T) {
if isInterfacePtr(V) {
r = "(" + check.interfacePtrError(V) + ")"
if Identical(m.typ, alt.typ) {
return check.sprintf("(%s has pointer receiver)", mname)
}
} else if isInterfacePtr(T) {
r = "(" + check.interfacePtrError(T) + ")"
return check.sprintf("(wrong type for %s)\n\t\thave %s\n\t\twant %s",
mname, check.funcString(alt), check.funcString(m))
}
if r == "" {
r = check.sprintf("(missing %s)", mname)
if isInterfacePtr(V) {
return "(" + check.interfacePtrError(V) + ")"
}
return r
if isInterfacePtr(T) {
return "(" + check.interfacePtrError(T) + ")"
}
return check.sprintf("(missing %s)", mname)
}
func isInterfacePtr(T Type) bool {
@ -440,6 +399,7 @@ func isInterfacePtr(T Type) bool {
return p != nil && IsInterface(p.base)
}
// check may be nil.
func (check *Checker) interfacePtrError(T Type) string {
assert(isInterfacePtr(T))
if p, _ := under(T).(*Pointer); isTypeParam(p.base) {
@ -448,6 +408,18 @@ func (check *Checker) interfacePtrError(T Type) string {
return check.sprintf("type %s is pointer to interface, not interface", T)
}
// funcString returns a string of the form name + signature for f.
// check may be nil.
func (check *Checker) funcString(f *Func) string {
buf := bytes.NewBufferString(f.name)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
WriteSignature(buf, f.typ.(*Signature), qf)
return buf.String()
}
// assertableTo reports whether a value of type V can be asserted to have type T.
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
// method required by V and whether it is missing or just has the wrong type.
@ -513,28 +485,11 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
}
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
// If foldCase is true, method names are considered equal if they are equal with case folding.
func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
if name != "_" {
for i, m := range methods {
if m.sameId(pkg, name) {
return i, m
}
}
}
return -1, nil
}
// lookupMethodFold is like lookupMethod, but if checkFold is true, it matches a method
// name if the names are equal with case folding.
func lookupMethodFold(methods []*Func, pkg *Package, name string, checkFold bool) (int, *Func) {
if name != "_" {
for i, m := range methods {
if m.name != name && !(checkFold && strings.EqualFold(m.name, name)) {
continue
}
// Use m.name, since we've already checked that m.name and
// name are equal with folding.
if m.sameId(pkg, m.name) {
if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) {
return i, m
}
}

View File

@ -41,20 +41,20 @@ func (l *methodList) isLazy() bool {
// panics if the receiver is lazy.
func (l *methodList) Add(m *Func) {
assert(!l.isLazy())
if i, _ := lookupMethod(l.methods, m.pkg, m.name); i < 0 {
if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 {
l.methods = append(l.methods, m)
}
}
// LookupFold looks up the method identified by pkg and name in the receiver.
// LookupFold panics if the receiver is lazy. If checkFold is true, it matches
// a method name if the names are equal with case folding.
func (l *methodList) LookupFold(pkg *Package, name string, checkFold bool) (int, *Func) {
// Lookup looks up the method identified by pkg and name in the receiver.
// Lookup panics if the receiver is lazy. If foldCase is true, method names
// are considered equal if they are equal with case folding.
func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) {
assert(!l.isLazy())
if l == nil {
return -1, nil
}
return lookupMethodFold(l.methods, pkg, name, checkFold)
return lookupMethod(l.methods, pkg, name, foldCase)
}
// Len returns the length of the method list.

View File

@ -297,12 +297,12 @@ func (n *Named) setUnderlying(typ Type) {
}
}
func (n *Named) lookupMethodFold(pkg *Package, name string, checkFold bool) (int, *Func) {
func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
n.resolve(nil)
// If n is an instance, we may not have yet instantiated all of its methods.
// Look up the method index in orig, and only instantiate method at the
// matching index (if any).
i, _ := n.orig.methods.LookupFold(pkg, name, checkFold)
i, _ := n.orig.methods.Lookup(pkg, name, foldCase)
if i < 0 {
return -1, nil
}

View File

@ -102,10 +102,11 @@ func isGeneric(t Type) bool {
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
return comparable(T, nil)
return comparable(T, nil, nil)
}
func comparable(T Type, seen map[Type]bool) bool {
// If reportf != nil, it may be used to report why T is not comparable.
func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
@ -123,13 +124,22 @@ func comparable(T Type, seen map[Type]bool) bool {
return true
case *Struct:
for _, f := range t.fields {
if !comparable(f.typ, seen) {
if !comparable(f.typ, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
return false
}
}
return true
case *Array:
return comparable(t.elem, seen)
if !comparable(t.elem, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
return false
}
return true
case *Interface:
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
}

View File

@ -239,7 +239,7 @@ L:
}
// Order matters: By comparing v against x, error positions are at the case values.
res := v // keep original v unchanged
check.comparison(&res, x, syntax.Eql)
check.comparison(&res, x, syntax.Eql, true)
if res.mode == invalid {
continue L
}
@ -274,7 +274,8 @@ func (check *Checker) isNil(e syntax.Expr) bool {
return false
}
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) {
// If the type switch expression is invalid, x is nil.
func (check *Checker) caseTypes(x *operand, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) {
var dummy operand
L:
for _, e := range types {
@ -305,8 +306,8 @@ L:
}
}
seen[T] = e
if T != nil && xtyp != nil {
check.typeAssertion(e, x, xtyp, T, true)
if x != nil && T != nil {
check.typeAssertion(e, x, T, true)
}
}
return
@ -733,12 +734,13 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
}
// TODO(gri) we may want to permit type switches on type parameter values at some point
var xtyp *Interface
var sx *operand // switch expression against which cases are compared against; nil if invalid
if isTypeParam(x.typ) {
check.errorf(&x, "cannot use type switch on type parameter value %s", &x)
} else {
xtyp, _ = under(x.typ).(*Interface)
if xtyp == nil {
if _, ok := under(x.typ).(*Interface); ok {
sx = &x
} else {
check.errorf(&x, "%s is not an interface", &x)
}
}
@ -758,7 +760,7 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
}
// Check each type in this type switch case.
cases := unpackExpr(clause.Cases)
T := check.caseTypes(&x, xtyp, cases, seen)
T := check.caseTypes(sx, cases, seen)
check.openScopeUntil(clause, end, "case")
// If lhs exists, declare a corresponding variable in the case-local scope.
if lhs != nil {

View File

@ -9,8 +9,8 @@ package expr2
func _bool() {
const t = true == true
const f = true == false
_ = t /* ERROR "cannot compare" */ < f
_ = 0 /* ERROR "mismatched types untyped int and untyped bool" */ == t
_ = t /* ERROR cannot compare */ < f
_ = 0 /* ERROR mismatched types untyped int and untyped bool */ == t
var b bool
var x, y float32
b = x < y
@ -20,7 +20,7 @@ func _bool() {
// corner cases
var (
v0 = nil /* ERROR "cannot compare" */ == nil
v0 = nil == nil // ERROR operator == not defined on untyped nil
)
func arrays() {
@ -40,7 +40,7 @@ func arrays() {
_ = c /* ERROR mismatched types */ == d
var e [10]func() int
_ = e /* ERROR == not defined */ == e
_ = e /* ERROR \[10\]func\(\) int cannot be compared */ == e
}
func structs() {
@ -79,8 +79,8 @@ func structs() {
func pointers() {
// nil
_ = nil /* ERROR == not defined */ == nil
_ = nil /* ERROR != not defined */ != nil
_ = nil == nil // ERROR operator == not defined on untyped nil
_ = nil != nil // ERROR operator != not defined on untyped nil
_ = nil /* ERROR < not defined */ < nil
_ = nil /* ERROR <= not defined */ <= nil
_ = nil /* ERROR > not defined */ > nil
@ -211,16 +211,16 @@ func interfaces() {
// issue #28164
// testcase from issue
_ = interface /* ERROR cannot compare */ {}(nil) == []int(nil)
_ = interface{}(nil) == [ /* ERROR slice can only be compared to nil */ ]int(nil)
// related cases
var e interface{}
var s []int
var x int
_ = e /* ERROR cannot compare */ == s
_ = s /* ERROR cannot compare */ == e
_ = e /* ERROR cannot compare */ < x
_ = x /* ERROR cannot compare */ < e
_ = e == s // ERROR slice can only be compared to nil
_ = s /* ERROR slice can only be compared to nil */ == e
_ = e /* ERROR operator < not defined on interface */ < x
_ = x < e // ERROR operator < not defined on interface
}
func slices() {
@ -231,7 +231,7 @@ func slices() {
_ = s /* ERROR < not defined */ < nil
// slices are not otherwise comparable
_ = s /* ERROR == not defined */ == s
_ = s /* ERROR slice can only be compared to nil */ == s
_ = s /* ERROR < not defined */ < s
}
@ -243,7 +243,7 @@ func maps() {
_ = m /* ERROR < not defined */ < nil
// maps are not otherwise comparable
_ = m /* ERROR == not defined */ == m
_ = m /* ERROR map can only be compared to nil */ == m
_ = m /* ERROR < not defined */ < m
}
@ -255,6 +255,6 @@ func funcs() {
_ = f /* ERROR < not defined */ < nil
// funcs are not otherwise comparable
_ = f /* ERROR == not defined */ == f
_ = f /* ERROR func can only be compared to nil */ == f
_ = f /* ERROR < not defined */ < f
}

View File

@ -131,42 +131,42 @@ func issue10260() {
)
var x I1
x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
_ = x. /* ERROR impossible type assertion: x.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ (T1)
x = T1 /* ERROR cannot use T1{} .* as I1 value in assignment: T1 does not implement I1 \(method foo has pointer receiver\) */ {}
_ = x /* ERROR impossible type assertion: x\.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ .(T1)
T1{}.foo /* ERROR cannot call pointer method foo on T1 */ ()
x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ ()
_ = i2. /* ERROR impossible type assertion: i2.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\thave func\(\)\n\twant func\(x int\) */ (*T1)
_ = i2 /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ .(*T1)
i1 = i0 /* ERROR cannot use .* missing method foo */
i1 = t0 /* ERROR cannot use .* missing method foo */
i1 = i2 /* ERROR cannot use .* wrong type for method foo */
i1 = t2 /* ERROR cannot use .* wrong type for method foo */
i2 = i1 /* ERROR cannot use .* wrong type for method foo */
i2 = t1 /* ERROR cannot use .* wrong type for method foo */
i1 = i0 /* ERROR cannot use i0 .* as I1 value in assignment: I0 does not implement I1 \(missing method foo\) */
i1 = t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */
i1 = i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i1 = t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i2 = i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
i2 = t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
_ = func() I1 { return i0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return t0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return t2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return i1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return t1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return i0 /* ERROR cannot use i0 .* as I1 value in return statement: I0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I1 { return t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I2 { return i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
_ = func() I2 { return t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
// a few more - less exhaustive now
f := func(I1, I2){}
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo \(have func\(\), want func\(x int\)\) */ )
f(i0 /* ERROR missing method foo */ , i1 /* ERROR wrong type for method foo */ )
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = []I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = []I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = [...]I1{i0 /* ERROR cannot use i0 .* as I1 value in array or slice literal: I0 does not implement I1 \(missing method foo\) */ }
_ = [...]I1{i2 /* ERROR cannot use i2 .* as I1 value in array or slice literal: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = []I1{i0 /* ERROR missing method foo */ }
_ = []I1{i2 /* ERROR wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR wrong type for method foo */ }
make(chan I1) <- i0 /* ERROR I0 does not implement I1: missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo \(have func\(x int\), want func\(\)\) */
make(chan I1) <- i0 /* ERROR missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo */
}
// Check that constants representable as integers are in integer form

View File

@ -429,7 +429,7 @@ func switches0() {
switch int32(x) {
case 1, 2:
case x /* ERROR "cannot compare" */ :
case x /* ERROR "invalid case x in switch on int32\(x\) \(mismatched types int and int32\)" */ :
}
switch x {

View File

@ -30,7 +30,7 @@ func _() {
}
switch (func())(nil) {
case f /* ERROR cannot compare */ :
case f /* ERROR invalid case f in switch on .* \(func can only be compared to nil\) */ :
}
switch nil /* ERROR use of untyped nil in switch expression */ {

View File

@ -8,8 +8,6 @@
package go1_17
import "constraints"
type T[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ] struct{}
// for init (and main, but we're not in package main) we should only get one error
@ -57,5 +55,3 @@ type (
_ = C1
_ = C2
)
type Ordered constraints /* ERROR using type constraint constraints\.Ordered requires go1\.18 or later */ .Ordered

View File

@ -10,7 +10,7 @@ func _[P comparable](x, y P) {
_ = y == x
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P comparable](x P, y any) {
@ -19,23 +19,23 @@ func _[P comparable](x P, y any) {
_ = y == x
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P any](x, y P) {
_ = x /* ERROR P is not comparable */ == x
_ = x /* ERROR P is not comparable */ == y
_ = y /* ERROR P is not comparable */ == x
_ = y /* ERROR P is not comparable */ == y
_ = x /* ERROR type parameter P is not comparable with == */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == y
_ = y /* ERROR type parameter P is not comparable with == */ == x
_ = y /* ERROR type parameter P is not comparable with == */ == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P any](x P, y any) {
_ = x /* ERROR P is not comparable */ == x
_ = x /* ERROR P is not comparable */ == y
_ = y /* ERROR P is not comparable */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == y
_ = y == x // ERROR type parameter P is not comparable with ==
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}

View File

@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is tested when running "go test -run Manual"
// without source arguments. Use for one-off debugging.
package p
type T1 interface{ M() }
@ -23,7 +20,7 @@ type T2 interface{ M() }
func F2() T2
var _ = F2(). /* ERROR impossible type assertion: F2\(\).\(\*X2\)\n\t\*X2 does not implement T2 \(missing method M\) */ (*X2)
var _ = F2 /* ERROR impossible type assertion: F2\(\)\.\(\*X2\)\n\t\*X2 does not implement T2 \(missing method M\) */ ().(*X2)
type X2 struct{}

View File

@ -9,7 +9,7 @@ type I[F any] interface {
}
func G[F any]() I[any] {
return g /* ERROR "missing method Q \(Q has pointer receiver\)" */ [F]{}
return g /* ERROR cannot use g\[F\]{} .* as I\[any\] value in return statement: g\[F\] does not implement I\[any\] \(method Q has pointer receiver\) */ [F]{}
}
type g[F any] struct{}

View File

@ -4,8 +4,11 @@
package p
import "constraints"
type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
func shl[I constraints.Integer](n int) I {
func shl[I Integer](n int) I {
return 1 << n
}

View File

@ -18,6 +18,6 @@ func (T2) foo() string { return "" }
func _() {
var i I
_ = i./* ERROR impossible type assertion: i.\(T1\)\n\tT1 does not implement I \(missing method Foo\)\n\t\thave foo\(\)\n\t\twant Foo\(\) */ (T1)
_ = i./* ERROR impossible type assertion: i.\(T2\)\n\tT2 does not implement I \(missing method Foo\)\n\t\thave foo\(\) string\n\t\twant Foo\(\) */ (T2)
_ = i /* ERROR impossible type assertion: i\.\(T1\)\n\tT1 does not implement I \(missing method Foo\)\n\t\thave foo\(\)\n\t\twant Foo\(\) */ .(T1)
_ = i /* ERROR impossible type assertion: i\.\(T2\)\n\tT2 does not implement I \(missing method Foo\)\n\t\thave foo\(\) string\n\t\twant Foo\(\) */ .(T2)
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type thing1 struct {
things []string
}
type thing2 struct {
things []thing1
}
func _() {
var a1, b1 thing1
_ = a1 /* ERROR struct containing \[\]string cannot be compared */ == b1
var a2, b2 thing2
_ = a2 /* ERROR struct containing \[\]thing1 cannot be compared */ == b2
}

View File

@ -0,0 +1,17 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _(x int, c string) {
switch x {
case c /* ERROR invalid case c in switch on x \(mismatched types string and int\) */ :
}
}
func _(x, c []int) {
switch x {
case c /* ERROR invalid case c in switch on x \(slice can only be compared to nil\) */ :
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _[P int]() {
_ = f[P]
}
func f[T int]() {}

View File

@ -0,0 +1,120 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package comparisons
type (
B int // basic type representative
A [10]func()
L []byte
S struct{ f []byte }
P *S
F func()
I interface{}
M map[string]int
C chan int
)
var (
b B
a A
l L
s S
p P
f F
i I
m M
c C
)
func _() {
_ = nil == nil // ERROR operator == not defined on untyped nil
_ = b == b
_ = a /* ERROR \[10\]func\(\) cannot be compared */ == a
_ = l /* ERROR slice can only be compared to nil */ == l
_ = s /* ERROR struct containing \[\]byte cannot be compared */ == s
_ = p == p
_ = f /* ERROR func can only be compared to nil */ == f
_ = i == i
_ = m /* ERROR map can only be compared to nil */ == m
_ = c == c
_ = b /* ERROR mismatched types */ == nil
_ = a /* ERROR mismatched types */ == nil
_ = l == nil
_ = s /* ERROR mismatched types */ == nil
_ = p == nil
_ = f == nil
_ = i == nil
_ = m == nil
_ = c == nil
_ = nil /* ERROR operator < not defined on untyped nil */ < nil
_ = b < b
_ = a /* ERROR operator < not defined on array */ < a
_ = l /* ERROR operator < not defined on slice */ < l
_ = s /* ERROR operator < not defined on struct */ < s
_ = p /* ERROR operator < not defined on pointer */ < p
_ = f /* ERROR operator < not defined on func */ < f
_ = i /* ERROR operator < not defined on interface */ < i
_ = m /* ERROR operator < not defined on map */ < m
_ = c /* ERROR operator < not defined on chan */ < c
}
func _[
B int,
A [10]func(),
L []byte,
S struct{ f []byte },
P *S,
F func(),
I interface{},
J comparable,
M map[string]int,
C chan int,
] (
b B,
a A,
l L,
s S,
p P,
f F,
i I,
j J,
m M,
c C,
) {
_ = b == b
_ = a /* ERROR type parameter A is not comparable with == */ == a
_ = l /* ERROR type parameter L is not comparable with == */ == l
_ = s /* ERROR type parameter S is not comparable with == */ == s
_ = p == p
_ = f /* ERROR type parameter F is not comparable with == */ == f
_ = i /* ERROR type parameter I is not comparable with == */ == i
_ = j == j
_ = m /* ERROR type parameter M is not comparable with == */ == m
_ = c == c
_ = b /* ERROR mismatched types */ == nil
_ = a /* ERROR mismatched types */ == nil
_ = l == nil
_ = s /* ERROR mismatched types */ == nil
_ = p == nil
_ = f == nil
_ = i /* ERROR mismatched types */ == nil
_ = j /* ERROR mismatched types */ == nil
_ = m == nil
_ = c == nil
_ = b < b
_ = a /* ERROR type parameter A is not comparable with < */ < a
_ = l /* ERROR type parameter L is not comparable with < */ < l
_ = s /* ERROR type parameter S is not comparable with < */ < s
_ = p /* ERROR type parameter P is not comparable with < */ < p
_ = f /* ERROR type parameter F is not comparable with < */ < f
_ = i /* ERROR type parameter I is not comparable with < */ < i
_ = j /* ERROR type parameter J is not comparable with < */ < j
_ = m /* ERROR type parameter M is not comparable with < */ < m
_ = c /* ERROR type parameter C is not comparable with < */ < c
}

View File

@ -124,8 +124,9 @@ func (t *TypeParam) iface() *Interface {
// compute type set if necessary
if ityp.tset == nil {
// use the (original) type bound position if we have one
pos := nopos
// pos is used for tracing output; start with the type parameter position.
pos := t.obj.pos
// use the (original or possibly instantiated) type bound position if we have one
if n, _ := bound.(*Named); n != nil {
pos = n.obj.pos
}

View File

@ -39,7 +39,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
return s.comparable
}
return s.is(func(t *term) bool {
return t != nil && comparable(t.typ, seen)
return t != nil && comparable(t.typ, seen, nil)
})
}
@ -58,9 +58,8 @@ func (s *_TypeSet) NumMethods() int { return len(s.methods) }
func (s *_TypeSet) Method(i int) *Func { return s.methods[i] }
// LookupMethod returns the index of and method with matching package and name, or (-1, nil).
func (s *_TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
// TODO(gri) s.methods is sorted - consider binary search
return lookupMethod(s.methods, pkg, name)
func (s *_TypeSet) LookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
return lookupMethod(s.methods, pkg, name, foldCase)
}
func (s *_TypeSet) String() string {

View File

@ -1117,9 +1117,9 @@ func (t *tester) cgoTest(dt *distTest) error {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto")
// Skip internal linking cases on linux/arm64 to support GCC-9.4 and above.
// Skip internal linking cases on arm64 to support GCC-9.4 and above.
// See issue #39466.
skipInternalLink := goarch == "arm64" && goos == "linux"
skipInternalLink := goarch == "arm64" && goos != "windows"
if t.internalLink() && !skipInternalLink {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal")

View File

@ -298,16 +298,13 @@ func (r *codeRepo) Latest() (*RevInfo, error) {
// If statVers is a valid module version, it is used for the Version field.
// Otherwise, the Version is derived from the passed-in info and recent tags.
func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
info2 := &RevInfo{
Name: info.Name,
Short: info.Short,
Time: info.Time,
}
// If this is a plain tag (no dir/ prefix)
// and the module path is unversioned,
// and if the underlying file tree has no go.mod,
// then allow using the tag with a +incompatible suffix.
//
// (If the version is +incompatible, then the go.mod file must not exist:
// +incompatible is not an ongoing opt-out from semantic import versioning.)
var canUseIncompatible func() bool
canUseIncompatible = func() bool {
var ok bool
@ -321,19 +318,12 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
return ok
}
invalidf := func(format string, args ...any) error {
return &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
Version: info2.Version,
Err: fmt.Errorf(format, args...),
},
}
}
// checkGoMod verifies that the go.mod file for the module exists or does not
// exist as required by info2.Version and the module path represented by r.
checkGoMod := func() (*RevInfo, error) {
// checkCanonical verifies that the canonical version v is compatible with the
// module path represented by r, adding a "+incompatible" suffix if needed.
//
// If statVers is also canonical, checkCanonical also verifies that v is
// either statVers or statVers with the added "+incompatible" suffix.
checkCanonical := func(v string) (*RevInfo, error) {
// If r.codeDir is non-empty, then the go.mod file must exist: the module
// author — not the module consumer, — gets to decide how to carve up the repo
// into modules.
@ -344,73 +334,91 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
// r.findDir verifies both of these conditions. Execute it now so that
// r.Stat will correctly return a notExistError if the go.mod location or
// declared module path doesn't match.
_, _, _, err := r.findDir(info2.Version)
_, _, _, err := r.findDir(v)
if err != nil {
// TODO: It would be nice to return an error like "not a module".
// Right now we return "missing go.mod", which is a little confusing.
return nil, &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
Version: info2.Version,
Version: v,
Err: notExistError{err: err},
},
}
}
// If the version is +incompatible, then the go.mod file must not exist:
// +incompatible is not an ongoing opt-out from semantic import versioning.
if strings.HasSuffix(info2.Version, "+incompatible") {
if !canUseIncompatible() {
if r.pathMajor != "" {
return nil, invalidf("+incompatible suffix not allowed: module path includes a major version suffix, so major version must match")
} else {
return nil, invalidf("+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required")
}
}
if err := module.CheckPathMajor(strings.TrimSuffix(info2.Version, "+incompatible"), r.pathMajor); err == nil {
return nil, invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(info2.Version))
invalidf := func(format string, args ...any) error {
return &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
Version: v,
Err: fmt.Errorf(format, args...),
},
}
}
return info2, nil
// Add the +incompatible suffix if needed or requested explicitly, and
// verify that its presence or absence is appropriate for this version
// (which depends on whether it has an explicit go.mod file).
if v == strings.TrimSuffix(statVers, "+incompatible") {
v = statVers
}
base := strings.TrimSuffix(v, "+incompatible")
var errIncompatible error
if !module.MatchPathMajor(base, r.pathMajor) {
if canUseIncompatible() {
v = base + "+incompatible"
} else {
if r.pathMajor != "" {
errIncompatible = invalidf("module path includes a major version suffix, so major version must match")
} else {
errIncompatible = invalidf("module contains a go.mod file, so module path must match major version (%q)", path.Join(r.pathPrefix, semver.Major(v)))
}
}
} else if strings.HasSuffix(v, "+incompatible") {
errIncompatible = invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(v))
}
if statVers != "" && statVers == module.CanonicalVersion(statVers) {
// Since the caller-requested version is canonical, it would be very
// confusing to resolve it to anything but itself, possibly with a
// "+incompatible" suffix. Error out explicitly.
if statBase := strings.TrimSuffix(statVers, "+incompatible"); statBase != base {
return nil, &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
Version: statVers,
Err: fmt.Errorf("resolves to version %v (%s is not a tag)", v, statBase),
},
}
}
}
if errIncompatible != nil {
return nil, errIncompatible
}
return &RevInfo{
Name: info.Name,
Short: info.Short,
Time: info.Time,
Version: v,
}, nil
}
// Determine version.
//
// If statVers is canonical, then the original call was repo.Stat(statVers).
// Since the version is canonical, we must not resolve it to anything but
// itself, possibly with a '+incompatible' annotation: we do not need to do
// the work required to look for an arbitrary pseudo-version.
if statVers != "" && statVers == module.CanonicalVersion(statVers) {
info2.Version = statVers
if module.IsPseudoVersion(info2.Version) {
if err := r.validatePseudoVersion(info, info2.Version); err != nil {
return nil, err
}
return checkGoMod()
if module.IsPseudoVersion(statVers) {
if err := r.validatePseudoVersion(info, statVers); err != nil {
return nil, err
}
if err := module.CheckPathMajor(info2.Version, r.pathMajor); err != nil {
if canUseIncompatible() {
info2.Version += "+incompatible"
return checkGoMod()
} else {
if vErr, ok := err.(*module.InvalidVersionError); ok {
// We're going to describe why the version is invalid in more detail,
// so strip out the existing “invalid version” wrapper.
err = vErr.Err
}
return nil, invalidf("module contains a go.mod file, so major version must be compatible: %v", err)
}
}
return checkGoMod()
return checkCanonical(statVers)
}
// statVers is empty or non-canonical, so we need to resolve it to a canonical
// version or pseudo-version.
// statVers is not a pseudo-version, so we need to either resolve it to a
// canonical version or verify that it is already a canonical tag
// (not a branch).
// Derive or verify a version from a code repo tag.
// Tag must have a prefix matching codeDir.
@ -441,71 +449,62 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
if v == "" || !strings.HasPrefix(trimmed, v) {
return "", false // Invalid or incomplete version (just vX or vX.Y).
}
if isRetracted(v) {
return "", false
}
if v == trimmed {
tagIsCanonical = true
}
if err := module.CheckPathMajor(v, r.pathMajor); err != nil {
if canUseIncompatible() {
return v + "+incompatible", tagIsCanonical
}
return "", false
}
return v, tagIsCanonical
}
// If the VCS gave us a valid version, use that.
if v, tagIsCanonical := tagToVersion(info.Version); tagIsCanonical {
info2.Version = v
return checkGoMod()
if info, err := checkCanonical(v); err == nil {
return info, err
}
}
// Look through the tags on the revision for either a usable canonical version
// or an appropriate base for a pseudo-version.
var pseudoBase string
var (
highestCanonical string
pseudoBase string
)
for _, pathTag := range info.Tags {
v, tagIsCanonical := tagToVersion(pathTag)
if tagIsCanonical {
if statVers != "" && semver.Compare(v, statVers) == 0 {
// The user requested a non-canonical version, but the tag for the
// canonical equivalent refers to the same revision. Use it.
info2.Version = v
return checkGoMod()
if statVers != "" && semver.Compare(v, statVers) == 0 {
// The tag is equivalent to the version requested by the user.
if tagIsCanonical {
// This tag is the canonical form of the requested version,
// not some other form with extra build metadata.
// Use this tag so that the resolved version will match exactly.
// (If it isn't actually allowed, we'll error out in checkCanonical.)
return checkCanonical(v)
} else {
// Save the highest canonical tag for the revision. If we don't find a
// better match, we'll use it as the canonical version.
// The user explicitly requested something equivalent to this tag. We
// can't use the version from the tag directly: since the tag is not
// canonical, it could be ambiguous. For example, tags v0.0.1+a and
// v0.0.1+b might both exist and refer to different revisions.
//
// NOTE: Do not replace this with semver.Max. Despite the name,
// semver.Max *also* canonicalizes its arguments, which uses
// semver.Canonical instead of module.CanonicalVersion and thereby
// strips our "+incompatible" suffix.
if semver.Compare(info2.Version, v) < 0 {
info2.Version = v
}
// The tag is otherwise valid for the module, so we can at least use it as
// the base of an unambiguous pseudo-version.
//
// If multiple tags match, tagToVersion will canonicalize them to the same
// base version.
pseudoBase = v
}
}
// Save the highest non-retracted canonical tag for the revision.
// If we don't find a better match, we'll use it as the canonical version.
if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) {
if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible() {
highestCanonical = v
}
} else if v != "" && semver.Compare(v, statVers) == 0 {
// The user explicitly requested something equivalent to this tag. We
// can't use the version from the tag directly: since the tag is not
// canonical, it could be ambiguous. For example, tags v0.0.1+a and
// v0.0.1+b might both exist and refer to different revisions.
//
// The tag is otherwise valid for the module, so we can at least use it as
// the base of an unambiguous pseudo-version.
//
// If multiple tags match, tagToVersion will canonicalize them to the same
// base version.
pseudoBase = v
}
}
// If we found any canonical tag for the revision, return it.
// If we found a valid canonical tag for the revision, return it.
// Even if we found a good pseudo-version base, a canonical version is better.
if info2.Version != "" {
return checkGoMod()
if highestCanonical != "" {
return checkCanonical(highestCanonical)
}
// Find the highest tagged version in the revision's history, subject to
@ -528,11 +527,10 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
}
}
pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
pseudoBase, _ = tagToVersion(tag)
}
info2.Version = module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short)
return checkGoMod()
return checkCanonical(module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short))
}
// validatePseudoVersion checks that version has a major version compatible with
@ -556,10 +554,6 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
}
}()
if err := module.CheckPathMajor(version, r.pathMajor); err != nil {
return err
}
rev, err := module.PseudoVersionRev(version)
if err != nil {
return err

View File

@ -418,171 +418,204 @@ var codeRepoTests = []codeRepoTest{
zipSum: "h1:JItBZ+gwA5WvtZEGEbuDL4lUttGtLrs53lmdurq3bOg=",
zipFileHash: "9ea9ae1673cffcc44b7fdd3cc89953d68c102449b46c982dbf085e4f2e394da5",
},
{
// Git branch with a semver name, +incompatible version, and no go.mod file.
vcs: "git",
path: "vcs-test.golang.org/go/mod/gitrepo1",
rev: "v2.3.4+incompatible",
err: `resolves to version v2.0.1+incompatible (v2.3.4 is not a tag)`,
},
{
// Git branch with a semver name, matching go.mod file, and compatible version.
vcs: "git",
path: "vcs-test.golang.org/git/semver-branch.git",
rev: "v1.0.0",
err: `resolves to version v0.1.1-0.20220202191944-09c4d8f6938c (v1.0.0 is not a tag)`,
},
{
// Git branch with a semver name, matching go.mod file, and disallowed +incompatible version.
// The version/tag mismatch takes precedence over the +incompatible mismatched.
vcs: "git",
path: "vcs-test.golang.org/git/semver-branch.git",
rev: "v2.0.0+incompatible",
err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
},
{
// Git branch with a semver name, matching go.mod file, and mismatched version.
// The version/tag mismatch takes precedence over the +incompatible mismatched.
vcs: "git",
path: "vcs-test.golang.org/git/semver-branch.git",
rev: "v2.0.0",
err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
},
{
// v3.0.0-devel is the same as tag v4.0.0-beta.1, but v4.0.0-beta.1 would
// not be allowed because it is incompatible and a go.mod file exists.
// The error message should refer to a valid pseudo-version, not the
// unusable semver tag.
vcs: "git",
path: "vcs-test.golang.org/git/semver-branch.git",
rev: "v3.0.0-devel",
err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`,
},
}
func TestCodeRepo(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tmpdir := t.TempDir()
tmpdir, err := os.MkdirTemp("", "modfetch-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
for _, tt := range codeRepoTests {
f := func(tt codeRepoTest) func(t *testing.T) {
return func(t *testing.T) {
t.Parallel()
if tt.vcs != "mod" {
testenv.MustHaveExecPath(t, tt.vcs)
}
t.Run("parallel", func(t *testing.T) {
for _, tt := range codeRepoTests {
f := func(tt codeRepoTest) func(t *testing.T) {
return func(t *testing.T) {
t.Parallel()
if tt.vcs != "mod" {
testenv.MustHaveExecPath(t, tt.vcs)
}
repo := Lookup("direct", tt.path)
repo := Lookup("direct", tt.path)
if tt.mpath == "" {
tt.mpath = tt.path
}
if mpath := repo.ModulePath(); mpath != tt.mpath {
t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
}
if tt.mpath == "" {
tt.mpath = tt.path
}
if mpath := repo.ModulePath(); mpath != tt.mpath {
t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
}
info, err := repo.Stat(tt.rev)
if err != nil {
if tt.err != "" {
if !strings.Contains(err.Error(), tt.err) {
t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
}
return
}
t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
}
info, err := repo.Stat(tt.rev)
if err != nil {
if tt.err != "" {
t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
}
if info.Version != tt.version {
t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
}
if info.Name != tt.name {
t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
}
if info.Short != tt.short {
t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
}
if !info.Time.Equal(tt.time) {
t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
if !strings.Contains(err.Error(), tt.err) {
t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
}
return
}
t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
}
if tt.err != "" {
t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
}
if info.Version != tt.version {
t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
}
if info.Name != tt.name {
t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
}
if info.Short != tt.short {
t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
}
if !info.Time.Equal(tt.time) {
t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
}
if tt.gomod != "" || tt.gomodErr != "" {
data, err := repo.GoMod(tt.version)
if err != nil && tt.gomodErr == "" {
t.Errorf("repo.GoMod(%q): %v", tt.version, err)
} else if err != nil && tt.gomodErr != "" {
if err.Error() != tt.gomodErr {
t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
}
} else if tt.gomodErr != "" {
t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
} else if string(data) != tt.gomod {
t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
}
}
needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
if tt.zip != nil || tt.zipErr != "" || needHash {
f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
if err != nil {
t.Fatalf("os.CreateTemp: %v", err)
}
zipfile := f.Name()
defer func() {
f.Close()
os.Remove(zipfile)
}()
var w io.Writer
var h hash.Hash
if needHash {
h = sha256.New()
w = io.MultiWriter(f, h)
} else {
w = f
}
err = repo.Zip(w, tt.version)
f.Close()
if err != nil {
if tt.zipErr != "" {
if err.Error() == tt.zipErr {
return
}
t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
}
t.Fatalf("repo.Zip(%q): %v", tt.version, err)
}
if tt.zipErr != "" {
t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
}
if tt.zip != nil {
prefix := tt.path + "@" + tt.version + "/"
z, err := zip.OpenReader(zipfile)
if err != nil {
t.Fatalf("open zip %s: %v", zipfile, err)
}
var names []string
for _, file := range z.File {
if !strings.HasPrefix(file.Name, prefix) {
t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
continue
}
names = append(names, file.Name[len(prefix):])
}
z.Close()
if !reflect.DeepEqual(names, tt.zip) {
t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
}
}
if needHash {
sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
if err != nil {
t.Errorf("repo.Zip(%q): %v", tt.version, err)
} else if sum != tt.zipSum {
t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
} else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
}
if tt.gomod != "" || tt.gomodErr != "" {
data, err := repo.GoMod(tt.version)
if err != nil && tt.gomodErr == "" {
t.Errorf("repo.GoMod(%q): %v", tt.version, err)
} else if err != nil && tt.gomodErr != "" {
if err.Error() != tt.gomodErr {
t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
}
} else if tt.gomodErr != "" {
t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
} else if string(data) != tt.gomod {
t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
}
}
}
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
if strings.HasPrefix(tt.path, vgotest1git) {
for vcs, alt := range altVgotests {
altTest := tt
altTest.vcs = vcs
altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
if strings.HasPrefix(altTest.mpath, vgotest1git) {
altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
if tt.zip != nil || tt.zipErr != "" || needHash {
f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
if err != nil {
t.Fatalf("os.CreateTemp: %v", err)
}
var m map[string]string
if alt == vgotest1hg {
m = hgmap
zipfile := f.Name()
defer func() {
f.Close()
os.Remove(zipfile)
}()
var w io.Writer
var h hash.Hash
if needHash {
h = sha256.New()
w = io.MultiWriter(f, h)
} else {
w = f
}
err = repo.Zip(w, tt.version)
f.Close()
if err != nil {
if tt.zipErr != "" {
if err.Error() == tt.zipErr {
return
}
t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
}
t.Fatalf("repo.Zip(%q): %v", tt.version, err)
}
if tt.zipErr != "" {
t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
}
if tt.zip != nil {
prefix := tt.path + "@" + tt.version + "/"
z, err := zip.OpenReader(zipfile)
if err != nil {
t.Fatalf("open zip %s: %v", zipfile, err)
}
var names []string
for _, file := range z.File {
if !strings.HasPrefix(file.Name, prefix) {
t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
continue
}
names = append(names, file.Name[len(prefix):])
}
z.Close()
if !reflect.DeepEqual(names, tt.zip) {
t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
}
}
if needHash {
sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
if err != nil {
t.Errorf("repo.Zip(%q): %v", tt.version, err)
} else if sum != tt.zipSum {
t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
} else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
}
}
altTest.version = remap(altTest.version, m)
altTest.name = remap(altTest.name, m)
altTest.short = remap(altTest.short, m)
altTest.rev = remap(altTest.rev, m)
altTest.err = remap(altTest.err, m)
altTest.gomodErr = remap(altTest.gomodErr, m)
altTest.zipErr = remap(altTest.zipErr, m)
altTest.zipSum = ""
altTest.zipFileHash = ""
t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
}
}
}
})
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
if strings.HasPrefix(tt.path, vgotest1git) {
for vcs, alt := range altVgotests {
altTest := tt
altTest.vcs = vcs
altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
if strings.HasPrefix(altTest.mpath, vgotest1git) {
altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
}
var m map[string]string
if alt == vgotest1hg {
m = hgmap
}
altTest.version = remap(altTest.version, m)
altTest.name = remap(altTest.name, m)
altTest.short = remap(altTest.short, m)
altTest.rev = remap(altTest.rev, m)
altTest.err = remap(altTest.err, m)
altTest.gomodErr = remap(altTest.gomodErr, m)
altTest.zipErr = remap(altTest.zipErr, m)
altTest.zipSum = ""
altTest.zipFileHash = ""
t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
}
}
}
}
var hgmap = map[string]string{

View File

@ -610,6 +610,9 @@ func UpdateWorkFile(wf *modfile.WorkFile) {
missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot
for _, d := range wf.Use {
if d.Path == "" {
continue // d is marked for deletion.
}
modRoot := d.Path
if d.ModulePath == "" {
missingModulePaths[d.Path] = modRoot

View File

@ -131,6 +131,7 @@ var validCompilerFlagsWithNextArg = []string{
"-D",
"-U",
"-I",
"-F",
"-framework",
"-include",
"-isysroot",

View File

@ -15,6 +15,7 @@ var goodCompilerFlags = [][]string{
{"-Ufoo"},
{"-Ufoo1"},
{"-F/Qt"},
{"-F", "/Qt"},
{"-I/"},
{"-I/etc/passwd"},
{"-I."},

View File

@ -115,17 +115,6 @@ func init() {
}
func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
anyFlags :=
*editGo != "" ||
*editJSON ||
*editPrint ||
*editFmt ||
len(workedits) > 0
if !anyFlags {
base.Fatalf("go: no flags specified (see 'go help work edit').")
}
if *editJSON && *editPrint {
base.Fatalf("go: cannot use both -json and -print")
}
@ -147,6 +136,21 @@ func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
}
}
if gowork == "" {
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using -workfile flag)")
}
anyFlags :=
*editGo != "" ||
*editJSON ||
*editPrint ||
*editFmt ||
len(workedits) > 0
if !anyFlags {
base.Fatalf("go: no flags specified (see 'go help work edit').")
}
workFile, err := modload.ReadWorkFile(gowork)
if err != nil {
base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err)

View File

@ -43,9 +43,11 @@ func init() {
}
func runSync(ctx context.Context, cmd *base.Command, args []string) {
modload.InitWorkfile()
modload.ForceUseModules = true
modload.InitWorkfile()
if modload.WorkFilePath() == "" {
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using -workfile flag)")
}
workGraph := modload.LoadModGraph(ctx, "")
_ = workGraph

View File

@ -49,6 +49,9 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
modload.InitWorkfile()
gowork = modload.WorkFilePath()
if gowork == "" {
base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using -workfile flag)")
}
workFile, err := modload.ReadWorkFile(gowork)
if err != nil {
base.Fatalf("go: %v", err)

View File

@ -142,6 +142,8 @@ var extraEnvKeys = []string{
"SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210
"WINDIR", // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711
"LD_LIBRARY_PATH", // must be preserved on Unix systems to find shared libraries
"LIBRARY_PATH", // allow override of non-standard static library paths
"C_INCLUDE_PATH", // allow override non-standard include paths
"CC", // don't lose user settings when invoking cgo
"GO_TESTING_GOTOOLS", // for gccgo testing
"GCCGO", // for gccgo testing

View File

@ -194,10 +194,10 @@ cp go.mod.orig go.mod
go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible
cd outside
! go list -m github.com/pierrec/lz4
stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
stderr '^go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
cd ..
! go list -m github.com/pierrec/lz4
stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
stderr '^go: github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
# A +incompatible pseudo-version is valid for a revision of the module
# that lacks a go.mod file.
@ -222,7 +222,7 @@ stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible'
# not resolve to a pseudo-version with a different major version.
cp go.mod.orig go.mod
! go get github.com/pierrec/lz4@v2.0.8
stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2'
stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
# An invalid +incompatible suffix for a canonical version should error out,
# not resolve to a pseudo-version.
@ -233,10 +233,10 @@ cp go.mod.orig go.mod
go mod edit -require github.com/pierrec/lz4@v2.0.8+incompatible
cd outside
! go list -m github.com/pierrec/lz4
stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
cd ..
! go list -m github.com/pierrec/lz4
stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
-- go.mod.orig --
module example.com

View File

@ -0,0 +1,20 @@
! go work use
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using -workfile flag\)$'
! go work use .
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using -workfile flag\)$'
! go work edit
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using -workfile flag\)$'
! go work edit -go=1.18
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using -workfile flag\)$'
! go work sync
stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using -workfile flag\)$'
-- go.mod --
module example
go 1.18
-- README.txt --
There is no go.work file here.

View File

@ -0,0 +1,17 @@
go work use -r .
cmp go.work go.work.want
-- go.mod --
module example
go 1.18
-- go.work --
go 1.18
use sub
-- go.work.want --
go 1.18
use .
-- sub/README.txt --
This directory no longer contains a go.mod file.

View File

@ -1,50 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package constraints defines a set of useful constraints to be used
// with type parameters.
package constraints
// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
~complex64 | ~complex128
}
// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
Integer | Float | ~string
}

View File

@ -1,117 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package constraints
import (
"bytes"
"fmt"
"internal/testenv"
"os"
"os/exec"
"path/filepath"
"testing"
)
type (
testSigned[T Signed] struct{ f T }
testUnsigned[T Unsigned] struct{ f T }
testInteger[T Integer] struct{ f T }
testFloat[T Float] struct{ f T }
testComplex[T Complex] struct{ f T }
testOrdered[T Ordered] struct{ f T }
)
// TestTypes passes if it compiles.
type TestTypes struct {
_ testSigned[int]
_ testSigned[int64]
_ testUnsigned[uint]
_ testUnsigned[uintptr]
_ testInteger[int8]
_ testInteger[uint8]
_ testInteger[uintptr]
_ testFloat[float32]
_ testComplex[complex64]
_ testOrdered[int]
_ testOrdered[float64]
_ testOrdered[string]
}
var prolog = []byte(`
package constrainttest
import "constraints"
type (
testSigned[T constraints.Signed] struct{ f T }
testUnsigned[T constraints.Unsigned] struct{ f T }
testInteger[T constraints.Integer] struct{ f T }
testFloat[T constraints.Float] struct{ f T }
testComplex[T constraints.Complex] struct{ f T }
testOrdered[T constraints.Ordered] struct{ f T }
)
`)
func TestFailure(t *testing.T) {
testenv.MustHaveGoBuild(t)
gocmd := testenv.GoToolPath(t)
tmpdir := t.TempDir()
if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module constraintest"), 0666); err != nil {
t.Fatal(err)
}
// Test for types that should not satisfy a constraint.
// For each pair of constraint and type, write a Go file
// var V constraint[type]
// For example,
// var V testSigned[uint]
// This should not compile, as testSigned (above) uses
// constraints.Signed, and uint does not satisfy that constraint.
// Therefore, the build of that code should fail.
for i, test := range []struct {
constraint, typ string
}{
{"testSigned", "uint"},
{"testUnsigned", "int"},
{"testInteger", "float32"},
{"testFloat", "int8"},
{"testComplex", "float64"},
{"testOrdered", "bool"},
} {
i := i
test := test
t.Run(fmt.Sprintf("%s %d", test.constraint, i), func(t *testing.T) {
t.Parallel()
name := fmt.Sprintf("go%d.go", i)
f, err := os.Create(filepath.Join(tmpdir, name))
if err != nil {
t.Fatal(err)
}
if _, err := f.Write(prolog); err != nil {
t.Fatal(err)
}
if _, err := fmt.Fprintf(f, "var V %s[%s]\n", test.constraint, test.typ); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
cmd := exec.Command(gocmd, "build", name)
cmd.Dir = tmpdir
if out, err := cmd.CombinedOutput(); err == nil {
t.Error("build succeeded, but expected to fail")
} else if len(out) > 0 {
t.Logf("%s", out)
const want = "does not implement"
if !bytes.Contains(out, []byte(want)) {
t.Errorf("output does not include %q", want)
}
} else {
t.Error("no error output, expected something")
}
})
}
}

View File

@ -89,6 +89,11 @@ func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
return specific.IsOnCurve(x, y)
}
if x.Sign() < 0 || x.Cmp(curve.P) >= 0 ||
y.Sign() < 0 || y.Cmp(curve.P) >= 0 {
return false
}
// y² = x³ - 3x + b
y2 := new(big.Int).Mul(y, y)
y2.Mod(y2, curve.P)

View File

@ -182,6 +182,61 @@ func testUnmarshalToLargeCoordinates(t *testing.T, curve Curve) {
}
}
// TestInvalidCoordinates tests big.Int values that are not valid field elements
// (negative or bigger than P). They are expected to return false from
// IsOnCurve, all other behavior is undefined.
func TestInvalidCoordinates(t *testing.T) {
testAllCurves(t, testInvalidCoordinates)
}
func testInvalidCoordinates(t *testing.T, curve Curve) {
checkIsOnCurveFalse := func(name string, x, y *big.Int) {
if curve.IsOnCurve(x, y) {
t.Errorf("IsOnCurve(%s) unexpectedly returned true", name)
}
}
p := curve.Params().P
_, x, y, _ := GenerateKey(curve, rand.Reader)
xx, yy := new(big.Int), new(big.Int)
// Check if the sign is getting dropped.
xx.Neg(x)
checkIsOnCurveFalse("-x, y", xx, y)
yy.Neg(y)
checkIsOnCurveFalse("x, -y", x, yy)
// Check if negative values are reduced modulo P.
xx.Sub(x, p)
checkIsOnCurveFalse("x-P, y", xx, y)
yy.Sub(y, p)
checkIsOnCurveFalse("x, y-P", x, yy)
// Check if positive values are reduced modulo P.
xx.Add(x, p)
checkIsOnCurveFalse("x+P, y", xx, y)
yy.Add(y, p)
checkIsOnCurveFalse("x, y+P", x, yy)
// Check if the overflow is dropped.
xx.Add(x, new(big.Int).Lsh(big.NewInt(1), 535))
checkIsOnCurveFalse("x+2⁵³⁵, y", xx, y)
yy.Add(y, new(big.Int).Lsh(big.NewInt(1), 535))
checkIsOnCurveFalse("x, y+2⁵³⁵", x, yy)
// Check if P is treated like zero (if possible).
// y^2 = x^3 - 3x + B
// y = mod_sqrt(x^3 - 3x + B)
// y = mod_sqrt(B) if x = 0
// If there is no modsqrt, there is no point with x = 0, can't test x = P.
if yy := new(big.Int).ModSqrt(curve.Params().B, p); yy != nil {
if !curve.IsOnCurve(big.NewInt(0), yy) {
t.Fatal("(0, mod_sqrt(B)) is not on the curve?")
}
checkIsOnCurveFalse("P, y", p, yy)
}
}
func TestMarshalCompressed(t *testing.T) {
t.Run("P-256/03", func(t *testing.T) {
data, _ := hex.DecodeString("031e3987d9f9ea9d7dd7155a56a86b2009e1e0ab332f962d10d8beb6406ab1ad79")

View File

@ -7,30 +7,13 @@
package main
import (
"bytes"
"crypto/elliptic"
"encoding/binary"
"fmt"
"go/format"
"log"
"os"
)
func main() {
buf := new(bytes.Buffer)
fmt.Fprint(buf, `
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Generated by gen_p256_table.go. DO NOT EDIT.
//go:build amd64 || arm64
package elliptic
`[1:])
// Generate precomputed p256 tables.
var pre [43][32 * 8]uint64
basePoint := []uint64{
@ -70,41 +53,21 @@ package elliptic
}
}
fmt.Fprint(buf, "const p256Precomputed = \"\" +\n\n")
var bin []byte
// Dump the precomputed tables, flattened, little-endian.
// These tables are used directly by assembly on little-endian platforms.
// Putting the data in a const string lets it be stored readonly.
// go:embedding the data into a string lets it be stored readonly.
for i := range &pre {
for j, v := range &pre[i] {
fmt.Fprintf(buf, "\"")
for _, v := range &pre[i] {
var u8 [8]byte
binary.LittleEndian.PutUint64(u8[:], v)
for _, b := range &u8 {
fmt.Fprintf(buf, "\\x%02x", b)
}
fmt.Fprintf(buf, "\"")
if i < len(pre)-1 || j < len(pre[i])-1 {
fmt.Fprint(buf, "+")
}
if j%8 == 7 {
fmt.Fprint(buf, "\n")
}
bin = append(bin, u8[:]...)
}
fmt.Fprint(buf, "\n")
}
src := buf.Bytes()
fmtsrc, fmterr := format.Source(src)
// If formatting failed, keep the original source for debugging.
if fmterr == nil {
src = fmtsrc
}
err := os.WriteFile("p256_asm_table.go", src, 0644)
err := os.WriteFile("p256_asm_table.bin", bin, 0644)
if err != nil {
log.Fatal(err)
}
if fmterr != nil {
log.Fatal(fmterr)
}
}

View File

@ -61,6 +61,9 @@ func p224PointFromAffine(x, y *big.Int) (p *nistec.P224Point, ok bool) {
if x.Sign() == 0 && y.Sign() == 0 {
return nistec.NewP224Point(), true
}
if x.Sign() < 0 || y.Sign() < 0 {
return nil, false
}
if x.BitLen() > 224 || y.BitLen() > 224 {
return nil, false
}

View File

@ -15,11 +15,15 @@
package elliptic
import (
_ "embed"
"math/big"
)
//go:generate go run -tags=tablegen gen_p256_table.go
//go:embed p256_asm_table.bin
var p256Precomputed string
type (
p256Curve struct {
*CurveParams

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,9 @@ func p384PointFromAffine(x, y *big.Int) (p *nistec.P384Point, ok bool) {
if x.Sign() == 0 && y.Sign() == 0 {
return nistec.NewP384Point(), true
}
if x.Sign() < 0 || y.Sign() < 0 {
return nil, false
}
if x.BitLen() > 384 || y.BitLen() > 384 {
return nil, false
}

View File

@ -71,6 +71,9 @@ func p521PointFromAffine(x, y *big.Int) (p *nistec.P521Point, ok bool) {
if x.Sign() == 0 && y.Sign() == 0 {
return nistec.NewP521Point(), true
}
if x.Sign() < 0 || y.Sign() < 0 {
return nil, false
}
if x.BitLen() > 521 || y.BitLen() > 521 {
return nil, false
}

View File

@ -51,9 +51,9 @@ func isPrintable(b byte) bool {
}
// parseASN1String parses the ASN.1 string types T61String, PrintableString,
// UTF8String, BMPString, and IA5String. This is mostly copied from the
// respective encoding/asn1.parse... methods, rather than just increasing
// the API surface of that package.
// UTF8String, BMPString, IA5String, and NumericString. This is mostly copied
// from the respective encoding/asn1.parse... methods, rather than just
// increasing the API surface of that package.
func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) {
switch tag {
case cryptobyte_asn1.T61String:
@ -93,6 +93,13 @@ func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) {
return "", errors.New("invalid IA5String")
}
return s, nil
case cryptobyte_asn1.Tag(asn1.TagNumericString):
for _, b := range value {
if !('0' <= b && b <= '9' || b == ' ') {
return "", errors.New("invalid NumericString")
}
}
return string(value), nil
}
return "", fmt.Errorf("unsupported string type: %v", tag)
}

View File

@ -0,0 +1,102 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"encoding/asn1"
"testing"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)
func TestParseASN1String(t *testing.T) {
tests := []struct {
name string
tag cryptobyte_asn1.Tag
value []byte
expected string
expectedErr string
}{
{
name: "T61String",
tag: cryptobyte_asn1.T61String,
value: []byte{80, 81, 82},
expected: string("PQR"),
},
{
name: "PrintableString",
tag: cryptobyte_asn1.PrintableString,
value: []byte{80, 81, 82},
expected: string("PQR"),
},
{
name: "PrintableString (invalid)",
tag: cryptobyte_asn1.PrintableString,
value: []byte{1, 2, 3},
expectedErr: "invalid PrintableString",
},
{
name: "UTF8String",
tag: cryptobyte_asn1.UTF8String,
value: []byte{80, 81, 82},
expected: string("PQR"),
},
{
name: "UTF8String (invalid)",
tag: cryptobyte_asn1.UTF8String,
value: []byte{255},
expectedErr: "invalid UTF-8 string",
},
{
name: "BMPString",
tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
value: []byte{80, 81},
expected: string("偑"),
},
{
name: "BMPString (invalid length)",
tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
value: []byte{255},
expectedErr: "invalid BMPString",
},
{
name: "IA5String",
tag: cryptobyte_asn1.IA5String,
value: []byte{80, 81},
expected: string("PQ"),
},
{
name: "IA5String (invalid)",
tag: cryptobyte_asn1.IA5String,
value: []byte{255},
expectedErr: "invalid IA5String",
},
{
name: "NumericString",
tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
value: []byte{49, 50},
expected: string("12"),
},
{
name: "NumericString (invalid)",
tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
value: []byte{80},
expectedErr: "invalid NumericString",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
out, err := parseASN1String(tc.tag, tc.value)
if err != nil && err.Error() != tc.expectedErr {
t.Fatalf("parseASN1String returned unexpected error: got %q, want %q", err, tc.expectedErr)
} else if err == nil && tc.expectedErr != "" {
t.Fatalf("parseASN1String didn't fail, expected: %s", tc.expectedErr)
}
if out != tc.expected {
t.Fatalf("parseASN1String returned unexpected value: got %q, want %q", out, tc.expected)
}
})
}
}

View File

@ -404,7 +404,7 @@ var depsRules = `
sync/atomic < crypto/internal/boring/fipstls;
encoding/binary, golang.org/x/sys/cpu, hash,
FMT, math/big,
FMT, math/big, embed,
CGO, crypto/internal/boring/sig, crypto/internal/boring/fipstls
< crypto
< crypto/subtle

View File

@ -977,7 +977,7 @@ func (p *parser) parseFuncType() *ast.FuncType {
pos := p.expect(token.FUNC)
tparams, params := p.parseParameters(true)
if tparams != nil {
p.error(tparams.Pos(), "function type cannot have type parameters")
p.error(tparams.Pos(), "function type must have no type parameters")
}
results := p.parseResult()
@ -1004,18 +1004,21 @@ func (p *parser) parseMethodSpec() *ast.Field {
p.exprLev--
if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
// generic method m[T any]
list := p.parseParameterList(name0, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
//
// Interface methods do not have type parameters. We parse them for a
// better error message and improved error recovery.
_ = p.parseParameterList(name0, token.RBRACK)
_ = p.expect(token.RBRACK)
p.error(lbrack, "interface method must have no type parameters")
// TODO(rfindley) refactor to share code with parseFuncType.
_, params := p.parseParameters(false)
results := p.parseResult()
idents = []*ast.Ident{ident}
typ = &ast.FuncType{
Func: token.NoPos,
TypeParams: tparams,
Params: params,
Results: results,
Func: token.NoPos,
Params: params,
Results: results,
}
} else {
// embedded instantiated type
@ -2655,6 +2658,12 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
ident := p.parseIdent()
tparams, params := p.parseParameters(true)
if recv != nil && tparams != nil {
// Method declarations do not have type parameters. We parse them for a
// better error message and improved error recovery.
p.error(tparams.Opening, "method must have no type parameters")
tparams = nil
}
results := p.parseResult()
var body *ast.BlockStmt

View File

@ -8,6 +8,7 @@ import (
"fmt"
"go/ast"
"go/token"
"strings"
)
const debugResolve = false
@ -24,6 +25,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
declErr: declErr,
topScope: pkgScope,
pkgScope: pkgScope,
depth: 1,
}
for _, decl := range file.Decls {
@ -45,7 +47,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
i++
} else if debugResolve {
pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos()
r.dump("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
}
}
file.Scope = r.pkgScope
@ -60,6 +62,7 @@ type resolver struct {
pkgScope *ast.Scope // pkgScope.Outer == nil
topScope *ast.Scope // top-most scope; may be pkgScope
unresolved []*ast.Ident // unresolved identifiers
depth int // scope depth
// Label scopes
// (maintained by open/close LabelScope)
@ -67,8 +70,8 @@ type resolver struct {
targetStack [][]*ast.Ident // stack of unresolved labels
}
func (r *resolver) dump(format string, args ...any) {
fmt.Println(">>> " + r.sprintf(format, args...))
func (r *resolver) trace(format string, args ...any) {
fmt.Println(strings.Repeat(". ", r.depth) + r.sprintf(format, args...))
}
func (r *resolver) sprintf(format string, args ...any) string {
@ -83,14 +86,16 @@ func (r *resolver) sprintf(format string, args ...any) string {
func (r *resolver) openScope(pos token.Pos) {
if debugResolve {
r.dump("opening scope @%v", pos)
r.trace("opening scope @%v", pos)
r.depth++
}
r.topScope = ast.NewScope(r.topScope)
}
func (r *resolver) closeScope() {
if debugResolve {
r.dump("closing scope")
r.depth--
r.trace("closing scope")
}
r.topScope = r.topScope.Outer
}
@ -117,21 +122,27 @@ func (r *resolver) closeLabelScope() {
func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
assert(ident.Obj == nil, "identifier already declared or resolved")
if ident.Obj != nil {
panic(fmt.Sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
}
obj := ast.NewObj(kind, ident.Name)
// remember the corresponding declaration for redeclaration
// errors and global variable resolution/typechecking phase
obj.Decl = decl
obj.Data = data
ident.Obj = obj
// Identifiers (for receiver type parameters) are written to the scope, but
// never set as the resolved object. See issue #50956.
if _, ok := decl.(*ast.Ident); !ok {
ident.Obj = obj
}
if ident.Name != "_" {
if debugResolve {
r.dump("declaring %s@%v", ident.Name, ident.Pos())
r.trace("declaring %s@%v", ident.Name, ident.Pos())
}
if alt := scope.Insert(obj); alt != nil && r.declErr != nil {
prevDecl := ""
if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", r.handle.Position(pos))
prevDecl = r.sprintf("\n\tprevious declaration at %v", pos)
}
r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
}
@ -153,7 +164,7 @@ func (r *resolver) shortVarDecl(decl *ast.AssignStmt) {
ident.Obj = obj
if ident.Name != "_" {
if debugResolve {
r.dump("declaring %s@%v", ident.Name, ident.Pos())
r.trace("declaring %s@%v", ident.Name, ident.Pos())
}
if alt := r.topScope.Insert(obj); alt != nil {
ident.Obj = alt // redeclaration
@ -180,7 +191,7 @@ var unresolved = new(ast.Object)
//
func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
if ident.Obj != nil {
panic(fmt.Sprintf("%s: identifier %s already declared or resolved", r.handle.Position(ident.Pos()), ident.Name))
panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
}
// '_' should never refer to existing declarations, because it has special
// handling in the spec.
@ -189,8 +200,15 @@ func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
}
for s := r.topScope; s != nil; s = s.Outer {
if obj := s.Lookup(ident.Name); obj != nil {
if debugResolve {
r.trace("resolved %v:%s to %v", ident.Pos(), ident.Name, obj)
}
assert(obj.Name != "", "obj with no name")
ident.Obj = obj
// Identifiers (for receiver type parameters) are written to the scope,
// but never set as the resolved object. See issue #50956.
if _, ok := obj.Decl.(*ast.Ident); !ok {
ident.Obj = obj
}
return
}
}
@ -227,7 +245,7 @@ func (r *resolver) walkStmts(list []ast.Stmt) {
func (r *resolver) Visit(node ast.Node) ast.Visitor {
if debugResolve && node != nil {
r.dump("node %T@%v", node, node.Pos())
r.trace("node %T@%v", node, node.Pos())
}
switch n := node.(type) {
@ -461,8 +479,7 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
r.openScope(n.Pos())
defer r.closeScope()
// Resolve the receiver first, without declaring.
r.resolveList(n.Recv)
r.walkRecv(n.Recv)
// Type parameters are walked normally: they can reference each other, and
// can be referenced by normal parameters.
@ -519,6 +536,52 @@ func (r *resolver) declareList(list *ast.FieldList, kind ast.ObjKind) {
}
}
func (r *resolver) walkRecv(recv *ast.FieldList) {
// If our receiver has receiver type parameters, we must declare them before
// trying to resolve the rest of the receiver, and avoid re-resolving the
// type parameter identifiers.
if recv == nil || len(recv.List) == 0 {
return // nothing to do
}
typ := recv.List[0].Type
if ptr, ok := typ.(*ast.StarExpr); ok {
typ = ptr.X
}
var declareExprs []ast.Expr // exprs to declare
var resolveExprs []ast.Expr // exprs to resolve
switch typ := typ.(type) {
case *ast.IndexExpr:
declareExprs = []ast.Expr{typ.Index}
resolveExprs = append(resolveExprs, typ.X)
case *ast.IndexListExpr:
declareExprs = typ.Indices
resolveExprs = append(resolveExprs, typ.X)
default:
resolveExprs = append(resolveExprs, typ)
}
for _, expr := range declareExprs {
if id, _ := expr.(*ast.Ident); id != nil {
r.declare(expr, nil, r.topScope, ast.Typ, id)
} else {
// The receiver type parameter expression is invalid, but try to resolve
// it anyway for consistency.
resolveExprs = append(resolveExprs, expr)
}
}
for _, expr := range resolveExprs {
if expr != nil {
ast.Walk(r, expr)
}
}
// The receiver is invalid, but try to resolve it anyway for consistency.
for _, f := range recv.List[1:] {
if f.Type != nil {
ast.Walk(r, f.Type)
}
}
}
func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) {
if list == nil {
return

View File

@ -94,9 +94,7 @@ var validWithTParamsOnly = []string{
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
`package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
`package p; type _[A, /* ERROR "expected ']', found ','" */ B any] interface { _(a A) B }`,
`package p; type _[A, /* ERROR "expected ']', found ','" */ B C[A, B]] interface { _(a A) B }`,
`package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 interface{}](x T1) T2`,
@ -110,10 +108,10 @@ var validWithTParamsOnly = []string{
`package p; var _ T[ /* ERROR "expected ';', found '\['" */ chan int]`,
// TODO(rfindley) this error message could be improved.
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _[T any](x T)`,
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _[T1, T2 any](x T)`,
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _(x T)`,
`package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _(x T)`,
`package p; func (R[P] /* ERROR "missing element type" */ ) _[T any]()`,
`package p; func (R[P] /* ERROR "missing element type" */ ) _()`,
`package p; func _(T[P] /* ERROR "missing element type" */ )`,
`package p; func _(T[P1, /* ERROR "expected ']', found ','" */ P2, P3 ])`,
`package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`,
@ -122,7 +120,7 @@ var validWithTParamsOnly = []string{
`package p; type _ interface{int| /* ERROR "expected ';'" */ float32; bool; m(); string;}`,
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`,
`package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`,
`package p; type _ interface { f[ /* ERROR "expected ';', found '\['" */ T any]() }`,
`package p; type _ interface { N[ /* ERROR "expected ';', found '\['" */ T] }`,
`package p; type T[P any /* ERROR "expected ']'" */ ] = T0`,
}
@ -235,20 +233,32 @@ var invalidNoTParamErrs = []string{
`package p; func _[ /* ERROR "expected '\(', found '\['" */ ]()`,
`package p; type _[A, /* ERROR "expected ']', found ','" */] struct{ A }`,
`package p; func _[ /* ERROR "expected '\(', found '\['" */ type P, *Q interface{}]()`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
`package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
`package p; func(*T[ /* ERROR "missing ',' in parameter list" */ e, e]) _()`,
}
// invalidTParamErrs holds invalid source code examples annotated with the
// error messages produced when ParseTypeParams is set.
var invalidTParamErrs = []string{
`package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
`package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
`package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`,
`package p; func _[]/* ERROR "empty type parameter list" */()`,
// TODO(rfindley) a better location would be after the ']'
`package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`,
`package p; type _[A /* ERROR "all type parameters must be named" */ ,] struct{ A }`,
// TODO(rfindley) this error is confusing.
`package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`,
`package p; func _[type /* ERROR "all type parameters must be named" */ P, *Q interface{}]()`,
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B any](a A) B`,
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C](a A) B`,
`package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C[A, B]](a A) B`,
`package p; func(*T[e, e /* ERROR "e redeclared" */ ]) _()`,
}
func TestInvalid(t *testing.T) {

19
src/go/parser/testdata/issue50427.go2 vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type T interface{ m[ /* ERROR "must have no type parameters" */ P any]() }
func _(t T) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = t
}
type S struct{}
func (S) m[ /* ERROR "must have no type parameters" */ P any]() {}
func _(s S) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s
}

View File

@ -25,10 +25,15 @@ func Add /* =@AddDecl */[T /* =@T */ Addable /* @Addable */](l /* =@l */, r /* =
type Receiver /* =@Receiver */[P /* =@P */ any] struct {}
type RP /* =@RP1 */ struct{}
// TODO(rFindley): make a decision on how/whether to resolve identifiers that
// refer to receiver type parameters, as is the case for the 'P' result
// parameter below.
func (r /* =@recv */ Receiver /* @Receiver */ [P]) m() P {}
//
// For now, we ensure that types are not incorrectly resolved when receiver
// type parameters are in scope.
func (r /* =@recv */ Receiver /* @Receiver */ [RP]) m(RP) RP {}
func f /* =@f */[T1 /* =@T1 */ interface{~[]T2 /* @T2 */}, T2 /* =@T2 */ any](
x /* =@x */ T1 /* @T1 */, T1 /* =@T1_duplicate */ y, // Note that this is a bug:
@ -41,3 +46,6 @@ func f /* =@f */[T1 /* =@T1 */ interface{~[]T2 /* @T2 */}, T2 /* =@T2 */ any](
T1 /* @T1 */ := 0
var t1var /* =@t1var */ T1 /* @T1 */
}
// From issue #39634
func(*ph1[e, e])h(d)

View File

@ -2362,3 +2362,61 @@ type Bad Bad // invalid type
}
}
}
func TestMissingMethodAlternative(t *testing.T) {
const src = `
package p
type T interface {
m()
}
type V0 struct{}
func (V0) m() {}
type V1 struct{}
type V2 struct{}
func (V2) m() int
type V3 struct{}
func (*V3) m()
type V4 struct{}
func (V4) M()
`
pkg, err := pkgFor("p.go", src, nil)
if err != nil {
t.Fatal(err)
}
T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
lookup := func(name string) (*Func, bool) {
return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true)
}
// V0 has method m with correct signature. Should not report wrongType.
method, wrongType := lookup("V0")
if method != nil || wrongType {
t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType)
}
checkMissingMethod := func(tname string, reportWrongType bool) {
method, wrongType := lookup(tname)
if method == nil || method.Name() != "m" || wrongType != reportWrongType {
t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType)
}
}
// V1 has no method m. Should not report wrongType.
checkMissingMethod("V1", false)
// V2 has method m with wrong signature type (ignoring receiver). Should report wrongType.
checkMissingMethod("V2", true)
// V3 has no method m but it exists on *V3. Should report wrongType.
checkMissingMethod("V3", true)
// V4 has no method m but has M. Should not report wrongType.
checkMissingMethod("V4", false)
}

View File

@ -143,9 +143,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// cap(x)
// len(x)
mode := invalid
var typ Type
var val constant.Value
switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
switch t := arrayPtrDeref(under(x.typ)).(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
@ -202,7 +201,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
}
}
if mode == invalid && typ != Typ[Invalid] {
if mode == invalid && under(x.typ) != Typ[Invalid] {
code := _InvalidCap
if id == _Len {
code = _InvalidLen
@ -211,12 +210,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
// record the signature before changing x.typ
if check.Types != nil && mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ))
}
x.mode = mode
x.typ = Typ[Int]
x.val = val
if check.Types != nil && mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
}
case _Close:
// close(c)

View File

@ -29,6 +29,8 @@ var builtinCalls = []struct {
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
{"cap", `type S []byte; var s S; _ = cap(s)`, `func(p.S) int`},
{"cap", `var s P; _ = cap(s)`, `func(P) int`},
{"len", `_ = len("foo")`, `invalid type`}, // constant
{"len", `var s string; _ = len(s)`, `func(string) int`},
@ -37,6 +39,8 @@ var builtinCalls = []struct {
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
{"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`},
{"len", `var s P; _ = len(s)`, `func(P) int`},
{"close", `var c chan int; close(c)`, `func(chan int)`},
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
@ -157,7 +161,7 @@ func TestBuiltinSignatures(t *testing.T) {
// parseGenericSrc in types2 is not necessary. We can just parse in testBuiltinSignature below.
func testBuiltinSignature(t *testing.T, name, src0, want string) {
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0)
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P ~[]byte]() { %s }`, src0)
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
t.Errorf("%s: %s", src0, err)

View File

@ -1385,10 +1385,7 @@ const (
// _InvalidMethodTypeParams occurs when methods have type parameters.
//
// Example:
// type T int
//
// func (T) m[P any]() {}
// It cannot be encountered with an AST parsed using go/parser.
_InvalidMethodTypeParams
// _MisplacedTypeParam occurs when a type parameter is used in a place where

View File

@ -63,8 +63,15 @@ func (check *Checker) markImports(pkg *Package) {
}
}
// check may be nil.
func (check *Checker) sprintf(format string, args ...any) string {
return sprintf(check.fset, check.qualifier, false, format, args...)
var fset *token.FileSet
var qf Qualifier
if check != nil {
fset = check.fset
qf = check.qualifier
}
return sprintf(fset, qf, false, format, args...)
}
func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {

View File

@ -279,7 +279,7 @@ func fib(x int) int {
//
// Types and Values of each expression:
// 4: 8 | string | type : string
// 6:15 | len | builtin : func(string) int
// 6:15 | len | builtin : func(fib.S) int
// 6:15 | len(b) | value : int
// 6:19 | b | var : fib.S
// 6:23 | S | type : fib.S

View File

@ -728,54 +728,84 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return target, nil, 0
}
func (check *Checker) comparison(x, y *operand, op token.Token) {
// If switchCase is true, the operator op is ignored.
func (check *Checker) comparison(x, y *operand, op token.Token, switchCase bool) {
if switchCase {
op = token.EQL
}
errOp := x // operand for which error is reported, if any
cause := "" // specific error cause, if any
// spec: "In any comparison, the first operand must be assignable
// to the type of the second operand, or vice versa."
err := ""
var code errorCode
xok, _ := x.assignableTo(check, y.typ, nil)
yok, _ := y.assignableTo(check, x.typ, nil)
if xok || yok {
equality := false
defined := false
switch op {
case token.EQL, token.NEQ:
// spec: "The equality operators == and != apply to operands that are comparable."
equality = true
defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case token.LSS, token.LEQ, token.GTR, token.GEQ:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
defined = allOrdered(x.typ) && allOrdered(y.typ)
default:
unreachable()
code := _MismatchedTypes
ok, _ := x.assignableTo(check, y.typ, nil)
if !ok {
ok, _ = y.assignableTo(check, x.typ, nil)
}
if !ok {
// Report the error on the 2nd operand since we only
// know after seeing the 2nd operand whether we have
// a type mismatch.
errOp = y
// For now, if we're not running the compiler, use the
// position of x to minimize changes to existing tests.
if !compilerErrorMessages {
errOp = x
}
if !defined {
if equality && (isTypeParam(x.typ) || isTypeParam(y.typ)) {
typ := x.typ
if isTypeParam(y.typ) {
typ = y.typ
}
err = check.sprintf("%s is not comparable", typ)
} else {
typ := x.typ
if x.isNil() {
typ = y.typ
}
err = check.sprintf("operator %s not defined on %s", op, typ)
cause = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
goto Error
}
// check if comparison is defined for operands
code = _UndefinedOp
switch op {
case token.EQL, token.NEQ:
// spec: "The equality operators == and != apply to operands that are comparable."
switch {
case x.isNil() || y.isNil():
// Comparison against nil requires that the other operand type has nil.
typ := x.typ
if x.isNil() {
typ = y.typ
}
code = _UndefinedOp
if !hasNil(typ) {
// This case should only be possible for "nil == nil".
// Report the error on the 2nd operand since we only
// know after seeing the 2nd operand whether we have
// an invalid comparison.
errOp = y
goto Error
}
case !Comparable(x.typ):
errOp = x
cause = check.incomparableCause(x.typ)
goto Error
case !Comparable(y.typ):
errOp = y
cause = check.incomparableCause(y.typ)
goto Error
}
} else {
err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
code = _MismatchedTypes
}
if err != "" {
check.errorf(x, code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
x.mode = invalid
return
case token.LSS, token.LEQ, token.GTR, token.GEQ:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
switch {
case !allOrdered(x.typ):
errOp = x
goto Error
case !allOrdered(y.typ):
errOp = y
goto Error
}
default:
unreachable()
}
// comparison is ok
if x.mode == constant_ && y.mode == constant_ {
x.val = constant.MakeBool(constant.Compare(x.val, op, y.val))
// The operands are never materialized; no need to update
@ -793,6 +823,73 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
// spec: "Comparison operators compare two operands and yield
// an untyped boolean value."
x.typ = Typ[UntypedBool]
return
Error:
// We have an offending operand errOp and possibly an error cause.
if cause == "" {
if isTypeParam(x.typ) || isTypeParam(y.typ) {
// TODO(gri) should report the specific type causing the problem, if any
if !isTypeParam(x.typ) {
errOp = y
}
cause = check.sprintf("type parameter %s is not comparable with %s", errOp.typ, op)
} else {
cause = check.sprintf("operator %s not defined on %s", op, check.kindString(errOp.typ)) // catch-all
}
}
if switchCase {
check.errorf(x, code, "invalid case %s in switch on %s (%s)", x.expr, y.expr, cause) // error position always at 1st operand
} else {
if compilerErrorMessages {
check.invalidOp(errOp, code, "%s %s %s (%s)", x.expr, op, y.expr, cause)
} else {
check.invalidOp(errOp, code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, cause)
}
}
x.mode = invalid
}
// incomparableCause returns a more specific cause why typ is not comparable.
// If there is no more specific cause, the result is "".
func (check *Checker) incomparableCause(typ Type) string {
switch under(typ).(type) {
case *Slice, *Signature, *Map:
return check.kindString(typ) + " can only be compared to nil"
}
// see if we can extract a more specific error
var cause string
comparable(typ, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
}
// kindString returns the type kind as a string.
func (check *Checker) kindString(typ Type) string {
switch under(typ).(type) {
case *Array:
return "array"
case *Slice:
return "slice"
case *Struct:
return "struct"
case *Pointer:
return "pointer"
case *Signature:
return "func"
case *Interface:
if isTypeParam(typ) {
return check.sprintf("type parameter %s", typ)
}
return "interface"
case *Map:
return "map"
case *Chan:
return "chan"
default:
return check.sprintf("%s", typ) // catch-all
}
}
// If e != nil, it must be the shift expression; it may be nil for non-constant shifts.
@ -1014,7 +1111,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
}
if isComparison(op) {
check.comparison(x, &y, op)
check.comparison(x, &y, op, false)
return
}
@ -1459,8 +1556,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
check.invalidOp(x, _InvalidAssert, "cannot use type assertion on type parameter value %s", x)
goto Error
}
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
if _, ok := under(x.typ).(*Interface); !ok {
check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
goto Error
}
@ -1475,7 +1571,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
if T == Typ[Invalid] {
goto Error
}
check.typeAssertion(x, x, xtyp, T)
check.typeAssertion(e, x, T, false)
x.mode = commaok
x.typ = T
@ -1579,23 +1675,21 @@ func keyVal(x constant.Value) any {
return x
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, T Type) {
method, wrongType := check.assertableTo(xtyp, T)
// typeAssertion checks x.(T). The type of x must be an interface.
func (check *Checker) typeAssertion(e ast.Expr, x *operand, T Type, typeSwitch bool) {
method, alt := check.assertableTo(under(x.typ).(*Interface), T)
if method == nil {
return // success
}
cause := check.missingMethodReason(T, x.typ, method, alt)
if typeSwitch {
check.errorf(e, _ImpossibleAssert, "impossible type switch case: %s\n\t%s cannot have dynamic type %s %s", e, x, T, cause)
return
}
var msg string
if wrongType != nil {
if Identical(method.typ, wrongType.typ) {
msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
} else {
msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
}
} else {
msg = "missing method " + method.name
}
check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
check.errorf(e, _ImpossibleAssert, "impossible type assertion: %s\n\t%s does not implement %s %s", e, T, x.typ, cause)
}
// expr typechecks expression e and initializes x with the expression value.

View File

@ -183,7 +183,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
if arg.mode == invalid {
// An error was reported earlier. Ignore this targ
// and continue, we may still be able to infer all
// targs resulting in fewer follon-on errors.
// targs resulting in fewer follow-on errors.
continue
}
if targ := arg.typ; isTyped(targ) {
@ -194,7 +194,12 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
errorf("type", par.typ, targ, arg)
return nil
}
} else {
} else if _, ok := par.typ.(*TypeParam); ok {
// Since default types are all basic (i.e., non-composite) types, an
// untyped argument will never match a composite parameter type; the
// only parameter type it can possibly match against is a *TypeParam.
// Thus, for untyped arguments we only need to look at parameter types
// that are single type parameters.
indices = append(indices, i)
}
}
@ -221,20 +226,17 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
// Some generic parameters with untyped arguments may have been given
// a type by now, we can ignore them.
for _, i := range indices {
par := params.At(i)
// Since untyped types are all basic (i.e., non-composite) types, an
// untyped argument will never match a composite parameter type; the
// only parameter type it can possibly match against is a *TypeParam.
// Thus, only consider untyped arguments for generic parameters that
// are not of composite types and which don't have a type inferred yet.
if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil {
tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of indices
// Only consider untyped arguments for which the corresponding type
// parameter doesn't have an inferred type yet.
if targs[tpar.index] == nil {
arg := args[i]
targ := Default(arg.typ)
// The default type for an untyped nil is untyped nil. We must not
// infer an untyped nil type as type parameter type. Ignore untyped
// nil by making sure all default argument types are typed.
if isTyped(targ) && !u.unify(par.typ, targ) {
errorf("default type", par.typ, targ, arg)
if isTyped(targ) && !u.unify(tpar, targ) {
errorf("default type", tpar, targ, arg)
return nil
}
}

View File

@ -135,6 +135,8 @@ func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool
func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) {
smap := makeSubstMap(tparams, targs)
for i, tpar := range tparams {
// Ensure that we have a (possibly implicit) interface as type bound (issue #51048).
tpar.iface()
// The type parameter bound is parameterized with the same type parameters
// as the instantiated type; before we can use it for bounds checking we
// need to instantiate it with the type arguments with which we instantiated
@ -160,25 +162,17 @@ func (check *Checker) implements(V, T Type) error {
return nil // avoid follow-on errors (see issue #49541 for an example)
}
var qf Qualifier
if check != nil {
qf = check.qualifier
}
errorf := func(format string, args ...any) error {
return errors.New(sprintf(nil, qf, false, format, args...))
return errors.New(check.sprintf(format, args...))
}
Ti, _ := Tu.(*Interface)
if Ti == nil {
var fset *token.FileSet
if check != nil {
fset = check.fset
}
var cause string
if isInterfacePtr(Tu) {
cause = sprintf(fset, qf, false, "type %s is pointer to interface, not interface", T)
cause = check.sprintf("type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(fset, qf, false, "%s is not an interface", T)
cause = check.sprintf("%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
@ -203,23 +197,8 @@ func (check *Checker) implements(V, T Type) error {
}
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
if check != nil && compilerErrorMessages {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
var cause string
if wrong != nil {
if Identical(m.typ, wrong.typ) {
cause = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
} else {
cause = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrong.typ, m.typ)
}
} else {
cause = "missing method " + m.Name()
}
return errorf("%s does not implement %s: %s", V, T, cause)
}
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
// If T is comparable, V must be comparable.

View File

@ -7,6 +7,7 @@
package types
import (
"bytes"
"strings"
)
@ -55,7 +56,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// not have found it for T (see also issue 8590).
if t, _ := T.(*Named); t != nil {
if p, _ := t.Underlying().(*Pointer); p != nil {
obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name, false)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
@ -63,7 +64,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
}
}
obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name)
obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name, false)
// If we didn't find anything and if we have a type parameter with a structural constraint,
// see if there is a matching field (but not a method, those need to be declared explicitly
@ -71,7 +72,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// are ok here because only fields are accepted as results.
if obj == nil && isTypeParam(T) {
if t := structuralType(T); t != nil {
obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name)
obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name, false)
if _, ok := obj.(*Var); !ok {
obj, index, indirect = nil, nil, false // accept fields (variables) only
}
@ -86,9 +87,11 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// indirectly via different packages.)
// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
// If foldCase is true, the lookup for methods will include looking for any method
// which case-folds to the same as 'name' (used for giving helpful error messages).
//
// The resulting object may not be fully type-checked.
func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
if name == "_" {
@ -142,7 +145,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
// look for a matching attached method
named.resolve(nil)
if i, m := named.lookupMethod(pkg, name); m != nil {
if i, m := named.lookupMethod(pkg, name, foldCase); m != nil {
// potential match
// caution: method may not have a proper signature yet
index = concat(e.index, i)
@ -189,7 +192,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
case *Interface:
// look for a matching method (interface may be a type parameter)
if i, m := t.typeSet().LookupMethod(pkg, name); m != nil {
if i, m := t.typeSet().LookupMethod(pkg, name, foldCase); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
@ -279,48 +282,38 @@ func lookupType(m map[Type]int, typ Type) (int, bool) {
// x is of interface type V).
//
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
m, typ := (*Checker)(nil).missingMethod(V, T, static)
return m, typ != nil
m, alt := (*Checker)(nil).missingMethod(V, T, static)
// Only report a wrong type if the alternative method has the same name as m.
return m, alt != nil && alt.name == m.name // alt != nil implies m != nil
}
// missingMethod is like MissingMethod but accepts a *Checker as
// receiver and an addressable flag.
// The receiver may be nil if missingMethod is invoked through
// an exported API call (such as MissingMethod), i.e., when all
// methods have been type-checked.
// If the type has the correctly named method, but with the wrong
// signature, the existing method is returned as well.
// To improve error messages, also report the wrong signature
// when the method exists on *V instead of V.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
// fast path for common case
if T.Empty() {
// missingMethod is like MissingMethod but accepts a *Checker as receiver.
// The receiver may be nil if missingMethod is invoked through an exported
// API call (such as MissingMethod), i.e., when all methods have been type-
// checked.
//
// If a method is missing on T but is found on *T, or if a method is found
// on T when looked up with case-folding, this alternative method is returned
// as the second result.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) {
if T.NumMethods() == 0 {
return
}
if ityp, _ := under(V).(*Interface); ityp != nil {
// TODO(gri) the methods are sorted - could do this more efficiently
// V is an interface
if u, _ := under(V).(*Interface); u != nil {
tset := u.typeSet()
for _, m := range T.typeSet().methods {
_, f := ityp.typeSet().LookupMethod(m.pkg, m.name)
_, f := tset.LookupMethod(m.pkg, m.name, false)
if f == nil {
if !static {
continue
}
return m, f
return m, nil
}
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
if ftyp.TypeParams().Len() > 0 {
panic("method with type parameters")
}
if !Identical(ftyp, mtyp) {
if !Identical(f.typ, m.typ) {
return m, f
}
}
@ -328,26 +321,22 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
// A concrete type implements T if it implements all methods of T.
// V is not an interface
for _, m := range T.typeSet().methods {
// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
// TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)?
obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name, false)
// Check if *V implements this method of T.
if obj == nil {
ptr := NewPointer(V)
obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name)
if obj != nil {
// methods may not have a fully set up signature yet
if check != nil {
check.objDecl(obj, nil)
}
return m, obj.(*Func)
// check if m is on *V, or on V with case-folding
found := obj != nil
if !found {
// TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument?
obj, _, _ = lookupFieldOrMethod(NewPointer(V), false, m.pkg, m.name, false)
if obj == nil {
obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true /* fold case */)
}
}
// we must have a method (not a field of matching function type)
// we must have a method (not a struct field)
f, _ := obj.(*Func)
if f == nil {
return m, nil
@ -358,17 +347,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
check.objDecl(f, nil)
}
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
if ftyp.TypeParams().Len() > 0 {
panic("method with type parameters")
}
if !Identical(ftyp, mtyp) {
if !found || !Identical(f.typ, m.typ) {
return m, f
}
}
@ -378,50 +357,41 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// missingMethodReason returns a string giving the detailed reason for a missing method m,
// where m is missing from V, but required by T. It puts the reason in parentheses,
// and may include more have/want info after that. If non-nil, wrongType is a relevant
// and may include more have/want info after that. If non-nil, alt is a relevant
// method that matches in some way. It may have the correct name, but wrong type, or
// it may have a pointer receiver.
func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string {
var r string
// it may have a pointer receiver, or it may have the correct name except wrong case.
// check may be nil.
func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string {
var mname string
if compilerErrorMessages {
if check != nil && compilerErrorMessages {
mname = m.Name() + " method"
} else {
mname = "method " + m.Name()
}
if wrongType != nil {
if m.Name() != wrongType.Name() {
// Note: this case can't happen because we don't look for alternative
// method spellings, unlike types2. Keep for symmetry with types2.
r = check.sprintf("(missing %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
} else if Identical(m.typ, wrongType.typ) {
r = check.sprintf("(%s has pointer receiver)", mname)
} else {
if compilerErrorMessages {
r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
} else {
r = check.sprintf("(wrong type for %s)\n\thave %s\n\twant %s",
mname, wrongType.typ, m.typ)
}
if alt != nil {
if m.Name() != alt.Name() {
return check.sprintf("(missing %s)\n\t\thave %s\n\t\twant %s",
mname, check.funcString(alt), check.funcString(m))
}
// This is a hack to print the function type without the leading
// 'func' keyword in the have/want printouts. We could change to have
// an extra formatting option for types2.Type that doesn't print out
// 'func'.
r = strings.Replace(r, "^^func", "", -1)
} else if IsInterface(T) {
if isInterfacePtr(V) {
r = "(" + check.interfacePtrError(V) + ")"
if Identical(m.typ, alt.typ) {
return check.sprintf("(%s has pointer receiver)", mname)
}
} else if isInterfacePtr(T) {
r = "(" + check.interfacePtrError(T) + ")"
return check.sprintf("(wrong type for %s)\n\t\thave %s\n\t\twant %s",
mname, check.funcString(alt), check.funcString(m))
}
if r == "" {
r = check.sprintf("(missing %s)", mname)
if isInterfacePtr(V) {
return "(" + check.interfacePtrError(V) + ")"
}
return r
if isInterfacePtr(T) {
return "(" + check.interfacePtrError(T) + ")"
}
return check.sprintf("(missing %s)", mname)
}
func isInterfacePtr(T Type) bool {
@ -429,6 +399,7 @@ func isInterfacePtr(T Type) bool {
return p != nil && IsInterface(p.base)
}
// check may be nil.
func (check *Checker) interfacePtrError(T Type) string {
assert(isInterfacePtr(T))
if p, _ := under(T).(*Pointer); isTypeParam(p.base) {
@ -437,6 +408,17 @@ func (check *Checker) interfacePtrError(T Type) string {
return check.sprintf("type %s is pointer to interface, not interface", T)
}
// check may be nil.
func (check *Checker) funcString(f *Func) string {
buf := bytes.NewBufferString(f.name)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
WriteSignature(buf, f.typ.(*Signature), qf)
return buf.String()
}
// assertableTo reports whether a value of type V can be asserted to have type T.
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
// method required by V and whether it is missing or just has the wrong type.
@ -502,10 +484,11 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
}
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
// If foldCase is true, method names are considered equal if they are equal with case folding.
func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
if name != "_" {
for i, m := range methods {
if m.sameId(pkg, name) {
if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) {
return i, m
}
}

View File

@ -41,19 +41,20 @@ func (l *methodList) isLazy() bool {
// panics if the receiver is lazy.
func (l *methodList) Add(m *Func) {
assert(!l.isLazy())
if i, _ := lookupMethod(l.methods, m.pkg, m.name); i < 0 {
if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 {
l.methods = append(l.methods, m)
}
}
// Lookup looks up the method identified by pkg and name in the receiver.
// Lookup panics if the receiver is lazy.
func (l *methodList) Lookup(pkg *Package, name string) (int, *Func) {
// Lookup panics if the receiver is lazy. If foldCase is true, method names
// are considered equal if they are equal with case folding.
func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) {
assert(!l.isLazy())
if l == nil {
return -1, nil
}
return lookupMethod(l.methods, pkg, name)
return lookupMethod(l.methods, pkg, name, foldCase)
}
// Len returns the length of the method list.

View File

@ -299,12 +299,12 @@ func (n *Named) setUnderlying(typ Type) {
}
}
func (n *Named) lookupMethod(pkg *Package, name string) (int, *Func) {
func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
n.resolve(nil)
// If n is an instance, we may not have yet instantiated all of its methods.
// Look up the method index in orig, and only instantiate method at the
// matching index (if any).
i, _ := n.orig.methods.Lookup(pkg, name)
i, _ := n.orig.methods.Lookup(pkg, name, foldCase)
if i < 0 {
return -1, nil
}

View File

@ -104,10 +104,11 @@ func isGeneric(t Type) bool {
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
return comparable(T, nil)
return comparable(T, nil, nil)
}
func comparable(T Type, seen map[Type]bool) bool {
// If reportf != nil, it may be used to report why T is not comparable.
func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
@ -125,13 +126,22 @@ func comparable(T Type, seen map[Type]bool) bool {
return true
case *Struct:
for _, f := range t.fields {
if !comparable(f.typ, seen) {
if !comparable(f.typ, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
return false
}
}
return true
case *Array:
return comparable(t.elem, seen)
if !comparable(t.elem, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
return false
}
return true
case *Interface:
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
}

View File

@ -248,7 +248,7 @@ L:
}
// Order matters: By comparing v against x, error positions are at the case values.
res := v // keep original v unchanged
check.comparison(&res, x, token.EQL)
check.comparison(&res, x, token.EQL, true)
if res.mode == invalid {
continue L
}
@ -281,7 +281,8 @@ func (check *Checker) isNil(e ast.Expr) bool {
return false
}
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
// If the type switch expression is invalid, x is nil.
func (check *Checker) caseTypes(x *operand, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
var dummy operand
L:
for _, e := range types {
@ -310,8 +311,8 @@ L:
}
}
seen[T] = e
if T != nil && xtyp != nil {
check.typeAssertion(e, x, xtyp, T)
if x != nil && T != nil {
check.typeAssertion(e, x, T, true)
}
}
return
@ -684,12 +685,13 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
return
}
// TODO(gri) we may want to permit type switches on type parameter values at some point
var xtyp *Interface
var sx *operand // switch expression against which cases are compared against; nil if invalid
if isTypeParam(x.typ) {
check.errorf(&x, _InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
} else {
xtyp, _ = under(x.typ).(*Interface)
if xtyp == nil {
if _, ok := under(x.typ).(*Interface); ok {
sx = &x
} else {
check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
}
}
@ -705,7 +707,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
continue
}
// Check each type in this type switch case.
T := check.caseTypes(&x, xtyp, clause.List, seen)
T := check.caseTypes(sx, clause.List, seen)
check.openScope(clause, "case")
// If lhs exists, declare a corresponding variable in the case-local scope.
if lhs != nil {

View File

@ -9,8 +9,8 @@ package expr2
func _bool() {
const t = true == true
const f = true == false
_ = t /* ERROR "cannot compare" */ < f
_ = 0 /* ERROR "mismatched types untyped int and untyped bool" */ == t
_ = t /* ERROR cannot compare */ < f
_ = 0 /* ERROR mismatched types untyped int and untyped bool */ == t
var b bool
var x, y float32
b = x < y
@ -20,7 +20,7 @@ func _bool() {
// corner cases
var (
v0 = nil /* ERROR "cannot compare" */ == nil
v0 = nil == nil // ERROR operator == not defined on untyped nil
)
func arrays() {
@ -40,7 +40,7 @@ func arrays() {
_ = c /* ERROR mismatched types */ == d
var e [10]func() int
_ = e /* ERROR == not defined */ == e
_ = e /* ERROR \[10\]func\(\) int cannot be compared */ == e
}
func structs() {
@ -79,8 +79,8 @@ func structs() {
func pointers() {
// nil
_ = nil /* ERROR == not defined */ == nil
_ = nil /* ERROR != not defined */ != nil
_ = nil == nil // ERROR operator == not defined on untyped nil
_ = nil != nil // ERROR operator != not defined on untyped nil
_ = nil /* ERROR < not defined */ < nil
_ = nil /* ERROR <= not defined */ <= nil
_ = nil /* ERROR > not defined */ > nil
@ -211,16 +211,16 @@ func interfaces() {
// issue #28164
// testcase from issue
_ = interface /* ERROR cannot compare */ {}(nil) == []int(nil)
_ = interface{}(nil) == [ /* ERROR slice can only be compared to nil */ ]int(nil)
// related cases
var e interface{}
var s []int
var x int
_ = e /* ERROR cannot compare */ == s
_ = s /* ERROR cannot compare */ == e
_ = e /* ERROR cannot compare */ < x
_ = x /* ERROR cannot compare */ < e
_ = e == s // ERROR slice can only be compared to nil
_ = s /* ERROR slice can only be compared to nil */ == e
_ = e /* ERROR operator < not defined on interface */ < x
_ = x < e // ERROR operator < not defined on interface
}
func slices() {
@ -231,7 +231,7 @@ func slices() {
_ = s /* ERROR < not defined */ < nil
// slices are not otherwise comparable
_ = s /* ERROR == not defined */ == s
_ = s /* ERROR slice can only be compared to nil */ == s
_ = s /* ERROR < not defined */ < s
}
@ -243,7 +243,7 @@ func maps() {
_ = m /* ERROR < not defined */ < nil
// maps are not otherwise comparable
_ = m /* ERROR == not defined */ == m
_ = m /* ERROR map can only be compared to nil */ == m
_ = m /* ERROR < not defined */ < m
}
@ -255,6 +255,6 @@ func funcs() {
_ = f /* ERROR < not defined */ < nil
// funcs are not otherwise comparable
_ = f /* ERROR == not defined */ == f
_ = f /* ERROR func can only be compared to nil */ == f
_ = f /* ERROR < not defined */ < f
}

View File

@ -131,42 +131,42 @@ func issue10260() {
)
var x I1
x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
_ = x /* ERROR .* cannot have dynamic type T1 \(missing method foo \(foo has pointer receiver\)\) */ .(T1)
x = T1 /* ERROR cannot use \(T1 literal\) .* as I1 value in assignment: T1 does not implement I1 \(method foo has pointer receiver\) */ {}
_ = x /* ERROR impossible type assertion: x\.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ .(T1)
T1{}.foo /* ERROR cannot call pointer method foo on T1 */ ()
x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ ()
_ = i2 /* ERROR i2 .* cannot have dynamic type \*T1 \(wrong type for method foo \(have func\(\), want func\(x int\)\)\) */ .(*T1)
_ = i2 /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ .(*T1)
i1 = i0 /* ERROR cannot use .* missing method foo */
i1 = t0 /* ERROR cannot use .* missing method foo */
i1 = i2 /* ERROR cannot use .* wrong type for method foo */
i1 = t2 /* ERROR cannot use .* wrong type for method foo */
i2 = i1 /* ERROR cannot use .* wrong type for method foo */
i2 = t1 /* ERROR cannot use .* wrong type for method foo */
i1 = i0 /* ERROR cannot use i0 .* as I1 value in assignment: I0 does not implement I1 \(missing method foo\) */
i1 = t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */
i1 = i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i1 = t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i2 = i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
i2 = t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
_ = func() I1 { return i0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return t0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return t2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return i1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return t1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return i0 /* ERROR cannot use i0 .* as I1 value in return statement: I0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I1 { return t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I2 { return i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
_ = func() I2 { return t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
// a few more - less exhaustive now
f := func(I1, I2){}
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo \(have func\(\), want func\(x int\)\) */ )
f(i0 /* ERROR missing method foo */ , i1 /* ERROR wrong type for method foo */ )
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = []I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = []I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = [...]I1{i0 /* ERROR cannot use i0 .* as I1 value in array or slice literal: I0 does not implement I1 \(missing method foo\) */ }
_ = [...]I1{i2 /* ERROR cannot use i2 .* as I1 value in array or slice literal: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = []I1{i0 /* ERROR missing method foo */ }
_ = []I1{i2 /* ERROR wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR wrong type for method foo */ }
make(chan I1) <- i0 /* ERROR I0 does not implement I1: missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo \(have func\(x int\), want func\(\)\) */
make(chan I1) <- i0 /* ERROR missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo */
}
// Check that constants representable as integers are in integer form

View File

@ -429,7 +429,7 @@ func switches0() {
switch int32(x) {
case 1, 2:
case x /* ERROR "cannot compare" */ :
case x /* ERROR "invalid case x in switch on int32\(x\) \(mismatched types int and int32\)" */ :
}
switch x {

View File

@ -329,8 +329,8 @@ func init[P /* ERROR func init must have no type parameters */ any]() {}
type T struct {}
func (T) m1() {}
func (T) m2[ /* ERROR methods cannot have type parameters */ _ any]() {}
func (T) m3[ /* ERROR methods cannot have type parameters */ P any]() {}
func (T) m2[ /* ERROR method must have no type parameters */ _ any]() {}
func (T) m3[ /* ERROR method must have no type parameters */ P any]() {}
// type inference across parameterized types
@ -391,25 +391,28 @@ func _[T any] (x T) {
// type parameters in methods (generalization)
type R0 struct{}
// Type Parameter lists are not allowed on methods, and are not produced by
// go/parser. The test cases below are preserved for consistency with types2,
// which produces an error but stores type parameters.
// type R0 struct{}
func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T) {}
func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() {} // scope of type parameters starts at "func"
// func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T) {}
// func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() {} // scope of type parameters starts at "func"
type R1[A, B any] struct{}
// type R1[A, B any] struct{}
func (_ R1[A, B]) m0(A, B)
func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T { panic(0) }
func (_ R1 /* ERROR not a generic type */ [R1, _]) _()
func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B) {}
// func (_ R1[A, B]) m0(A, B)
// func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T { panic(0) }
// func (_ R1 /* ERROR not a generic type */ [R1, _]) _()
// func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B) {}
func _() {
var r R1[int, string]
r.m1[rune](42, "foo", 'a')
r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */)
r.m1(42, "foo", 1.2) // using type inference
var _ float64 = r.m1(42, "foo", 1.2)
}
// func _() {
// var r R1[int, string]
// r.m1[rune](42, "foo", 'a')
// r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */)
// r.m1(42, "foo", 1.2) // using type inference
// var _ float64 = r.m1(42, "foo", 1.2)
// }
type I1[A any] interface {
m1(A)

View File

@ -85,7 +85,7 @@ func (t T25[A]) m1() {}
var x T25 /* ERROR without instantiation */ .m1
// crash 26
type T26 = interface{ F26[ /* ERROR methods cannot have type parameters */ Z any]() }
type T26 = interface{ F26[ /* ERROR interface method must have no type parameters */ Z any]() }
// The error messages on the line below differ from types2 because for backward
// compatibility go/parser must produce an IndexExpr with BadExpr index for the
// expression F26[].

View File

@ -30,7 +30,7 @@ func _() {
}
switch (func())(nil) {
case f /* ERROR cannot compare */ :
case f /* ERROR invalid case f in switch on .* \(func can only be compared to nil\) */ :
}
switch nil /* ERROR use of untyped nil in switch expression */ {

View File

@ -8,8 +8,6 @@
package go1_17
import "constraints"
type T[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ] struct{}
// for init (and main, but we're not in package main) we should only get one error
@ -57,5 +55,3 @@ type (
_ = C1
_ = C2
)
type Ordered constraints /* ERROR using type constraint constraints\.Ordered requires go1\.18 or later */ .Ordered

View File

@ -10,7 +10,7 @@ func _[P comparable](x, y P) {
_ = y == x
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P comparable](x P, y any) {
@ -19,23 +19,23 @@ func _[P comparable](x P, y any) {
_ = y == x
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P any](x, y P) {
_ = x /* ERROR P is not comparable */ == x
_ = x /* ERROR P is not comparable */ == y
_ = y /* ERROR P is not comparable */ == x
_ = y /* ERROR P is not comparable */ == y
_ = x /* ERROR type parameter P is not comparable with == */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == y
_ = y /* ERROR type parameter P is not comparable with == */ == x
_ = y /* ERROR type parameter P is not comparable with == */ == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}
func _[P any](x P, y any) {
_ = x /* ERROR P is not comparable */ == x
_ = x /* ERROR P is not comparable */ == y
_ = y /* ERROR P is not comparable */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == x
_ = x /* ERROR type parameter P is not comparable with == */ == y
_ = y == x // ERROR type parameter P is not comparable with ==
_ = y == y
_ = x /* ERROR operator < not defined on P */ < y
_ = x /* ERROR type parameter P is not comparable with < */ < y
}

View File

@ -0,0 +1,31 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type T1 interface{ M() }
func F1() T1
var _ = F1().(*X1 /* ERROR undeclared name: X1 */)
func _() {
switch F1().(type) {
case *X1 /* ERROR undeclared name: X1 */ :
}
}
type T2 interface{ M() }
func F2() T2
var _ = F2 /* ERROR impossible type assertion: F2\(\)\.\(\*X2\)\n\t\*X2 does not implement T2 \(missing method M\) */ ().(*X2)
type X2 struct{}
func _() {
switch F2().(type) {
case * /* ERROR impossible type switch case: \*X2\n\tF2\(\) \(value of type T2\) cannot have dynamic type \*X2 \(missing method M\) */ X2:
}
}

View File

@ -9,7 +9,7 @@ type I[F any] interface {
}
func G[F any]() I[any] {
return g /* ERROR "missing method Q \(Q has pointer receiver\)" */ [F]{}
return g /* ERROR cannot use \(g\[F\] literal\) .* as I\[any\] value in return statement: g\[F\] does not implement I\[any\] \(method Q has pointer receiver\) */ [F]{}
}
type g[F any] struct{}

View File

@ -4,8 +4,11 @@
package p
import "constraints"
type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
func shl[I constraints.Integer](n int) I {
func shl[I Integer](n int) I {
return 1 << n
}

View File

@ -0,0 +1,23 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
// The parser no longer parses type parameters for methods.
// In the past, type checking the code below led to a crash (#50427).
type T interface{ m[ /* ERROR "must have no type parameters" */ P any]() }
func _(t T) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = t /* ERROR "does not implement" */
}
type S struct{}
func (S) m[ /* ERROR "must have no type parameters" */ P any]() {}
func _(s S) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s /* ERROR "does not implement" */
}

View File

@ -18,6 +18,6 @@ func (T2) foo() string { return "" }
func _() {
var i I
_ = i/* ERROR i \(variable of type I\) cannot have dynamic type T1 \(missing method Foo\) */.(T1)
_ = i/* ERROR i \(variable of type I\) cannot have dynamic type T2 \(missing method Foo\) */.(T2)
_ = i /* ERROR impossible type assertion: i\.\(T1\)\n\tT1 does not implement I \(missing method Foo\)\n\t\thave foo\(\)\n\t\twant Foo\(\) */ .(T1)
_ = i /* ERROR impossible type assertion: i\.\(T2\)\n\tT2 does not implement I \(missing method Foo\)\n\t\thave foo\(\) string\n\t\twant Foo\(\) */ .(T2)
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type thing1 struct {
things []string
}
type thing2 struct {
things []thing1
}
func _() {
var a1, b1 thing1
_ = a1 /* ERROR struct containing \[\]string cannot be compared */ == b1
var a2, b2 thing2
_ = a2 /* ERROR struct containing \[\]thing1 cannot be compared */ == b2
}

View File

@ -0,0 +1,17 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _(x int, c string) {
switch x {
case c /* ERROR invalid case c in switch on x \(mismatched types string and int\) */ :
}
}
func _(x, c []int) {
switch x {
case c /* ERROR invalid case c in switch on x \(slice can only be compared to nil\) */ :
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _[P int]() {
_ = f[P]
}
func f[T int]() {}

View File

@ -0,0 +1,120 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package comparisons
type (
B int // basic type representative
A [10]func()
L []byte
S struct{ f []byte }
P *S
F func()
I interface{}
M map[string]int
C chan int
)
var (
b B
a A
l L
s S
p P
f F
i I
m M
c C
)
func _() {
_ = nil == nil // ERROR operator == not defined on untyped nil
_ = b == b
_ = a /* ERROR \[10\]func\(\) cannot be compared */ == a
_ = l /* ERROR slice can only be compared to nil */ == l
_ = s /* ERROR struct containing \[\]byte cannot be compared */ == s
_ = p == p
_ = f /* ERROR func can only be compared to nil */ == f
_ = i == i
_ = m /* ERROR map can only be compared to nil */ == m
_ = c == c
_ = b /* ERROR mismatched types */ == nil
_ = a /* ERROR mismatched types */ == nil
_ = l == nil
_ = s /* ERROR mismatched types */ == nil
_ = p == nil
_ = f == nil
_ = i == nil
_ = m == nil
_ = c == nil
_ = nil /* ERROR operator < not defined on untyped nil */ < nil
_ = b < b
_ = a /* ERROR operator < not defined on array */ < a
_ = l /* ERROR operator < not defined on slice */ < l
_ = s /* ERROR operator < not defined on struct */ < s
_ = p /* ERROR operator < not defined on pointer */ < p
_ = f /* ERROR operator < not defined on func */ < f
_ = i /* ERROR operator < not defined on interface */ < i
_ = m /* ERROR operator < not defined on map */ < m
_ = c /* ERROR operator < not defined on chan */ < c
}
func _[
B int,
A [10]func(),
L []byte,
S struct{ f []byte },
P *S,
F func(),
I interface{},
J comparable,
M map[string]int,
C chan int,
] (
b B,
a A,
l L,
s S,
p P,
f F,
i I,
j J,
m M,
c C,
) {
_ = b == b
_ = a /* ERROR type parameter A is not comparable with == */ == a
_ = l /* ERROR type parameter L is not comparable with == */ == l
_ = s /* ERROR type parameter S is not comparable with == */ == s
_ = p == p
_ = f /* ERROR type parameter F is not comparable with == */ == f
_ = i /* ERROR type parameter I is not comparable with == */ == i
_ = j == j
_ = m /* ERROR type parameter M is not comparable with == */ == m
_ = c == c
_ = b /* ERROR mismatched types */ == nil
_ = a /* ERROR mismatched types */ == nil
_ = l == nil
_ = s /* ERROR mismatched types */ == nil
_ = p == nil
_ = f == nil
_ = i /* ERROR mismatched types */ == nil
_ = j /* ERROR mismatched types */ == nil
_ = m == nil
_ = c == nil
_ = b < b
_ = a /* ERROR type parameter A is not comparable with < */ < a
_ = l /* ERROR type parameter L is not comparable with < */ < l
_ = s /* ERROR type parameter S is not comparable with < */ < s
_ = p /* ERROR type parameter P is not comparable with < */ < p
_ = f /* ERROR type parameter F is not comparable with < */ < f
_ = i /* ERROR type parameter I is not comparable with < */ < i
_ = j /* ERROR type parameter J is not comparable with < */ < j
_ = m /* ERROR type parameter M is not comparable with < */ < m
_ = c /* ERROR type parameter C is not comparable with < */ < c
}

View File

@ -5,7 +5,6 @@
package types
import (
"go/token"
"sync/atomic"
)
@ -127,8 +126,9 @@ func (t *TypeParam) iface() *Interface {
// compute type set if necessary
if ityp.tset == nil {
// use the (original) type bound position if we have one
pos := token.NoPos
// pos is used for tracing output; start with the type parameter position.
pos := t.obj.pos
// use the (original or possibly instantiated) type bound position if we have one
if n, _ := bound.(*Named); n != nil {
pos = n.obj.pos
}

View File

@ -37,7 +37,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
return s.comparable
}
return s.is(func(t *term) bool {
return t != nil && comparable(t.typ, seen)
return t != nil && comparable(t.typ, seen, nil)
})
}
@ -56,9 +56,8 @@ func (s *_TypeSet) NumMethods() int { return len(s.methods) }
func (s *_TypeSet) Method(i int) *Func { return s.methods[i] }
// LookupMethod returns the index of and method with matching package and name, or (-1, nil).
func (s *_TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
// TODO(gri) s.methods is sorted - consider binary search
return lookupMethod(s.methods, pkg, name)
func (s *_TypeSet) LookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
return lookupMethod(s.methods, pkg, name, foldCase)
}
func (s *_TypeSet) String() string {

Some files were not shown because too many files have changed in this diff Show More