diff options
Diffstat (limited to 'libgo/go/go')
212 files changed, 11813 insertions, 6203 deletions
diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index 337c87f..a74a827 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -193,14 +193,10 @@ func isDirective(c string) bool { // in a signature. // Field.Names is nil for unnamed parameters (parameter lists which only contain types) // and embedded struct fields. In the latter case, the field name is the type name. -// Field.Names contains a single name "type" for elements of interface type lists. -// Types belonging to the same type list share the same "type" identifier which also -// records the position of that keyword. -// type Field struct { Doc *CommentGroup // associated documentation; or nil - Names []*Ident // field/method/(type) parameter names, or type "type"; or nil - Type Expr // field/method/parameter type, type list type; or nil + Names []*Ident // field/method/(type) parameter names; or nil + Type Expr // field/method/parameter type; or nil Tag *BasicLit // field tag; or nil Comment *CommentGroup // line comments; or nil } @@ -228,11 +224,12 @@ func (f *Field) End() token.Pos { return token.NoPos } -// A FieldList represents a list of Fields, enclosed by parentheses or braces. +// A FieldList represents a list of Fields, enclosed by parentheses, +// curly braces, or square brackets. type FieldList struct { - Opening token.Pos // position of opening parenthesis/brace, if any + Opening token.Pos // position of opening parenthesis/brace/bracket, if any List []*Field // field list; or nil - Closing token.Pos // position of closing parenthesis/brace, if any + Closing token.Pos // position of closing parenthesis/brace/bracket, if any } func (f *FieldList) Pos() token.Pos { @@ -344,6 +341,15 @@ type ( Rbrack token.Pos // position of "]" } + // An IndexListExpr node represents an expression followed by multiple + // indices. + IndexListExpr struct { + X Expr // expression + Lbrack token.Pos // position of "[" + Indices []Expr // index expressions + Rbrack token.Pos // position of "]" + } + // A SliceExpr node represents an expression followed by slice indices. SliceExpr struct { X Expr // expression @@ -440,6 +446,14 @@ type ( // Pointer types are represented via StarExpr nodes. + // A FuncType node represents a function type. + FuncType struct { + Func token.Pos // position of "func" keyword (token.NoPos if there is no "func") + TypeParams *FieldList // type parameters; or nil + Params *FieldList // (incoming) parameters; non-nil + Results *FieldList // (outgoing) results; or nil + } + // An InterfaceType node represents an interface type. InterfaceType struct { Interface token.Pos // position of "interface" keyword @@ -479,6 +493,7 @@ func (x *CompositeLit) Pos() token.Pos { func (x *ParenExpr) Pos() token.Pos { return x.Lparen } func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() } func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() } +func (x *IndexListExpr) Pos() token.Pos { return x.X.Pos() } func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() } func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() } func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() } @@ -512,6 +527,7 @@ func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 } func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 } func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *IndexListExpr) End() token.Pos { return x.Rbrack + 1 } func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 } func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 } func (x *CallExpr) End() token.Pos { return x.Rparen + 1 } @@ -543,6 +559,7 @@ func (*CompositeLit) exprNode() {} func (*ParenExpr) exprNode() {} func (*SelectorExpr) exprNode() {} func (*IndexExpr) exprNode() {} +func (*IndexListExpr) exprNode() {} func (*SliceExpr) exprNode() {} func (*TypeAssertExpr) exprNode() {} func (*CallExpr) exprNode() {} @@ -892,6 +909,16 @@ type ( Values []Expr // initial values; or nil Comment *CommentGroup // line comments; or nil } + + // A TypeSpec node represents a type declaration (TypeSpec production). + TypeSpec struct { + Doc *CommentGroup // associated documentation; or nil + Name *Ident // type name + TypeParams *FieldList // type parameters; or nil + Assign token.Pos // position of '=', if any + Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes + Comment *CommentGroup // line comments; or nil + } ) // Pos and End implementations for spec nodes. @@ -968,8 +995,6 @@ type ( Name *Ident // function/method name Type *FuncType // function signature: type and value parameters, results, and position of "func" keyword Body *BlockStmt // function body; or nil for external (non-Go) function - // TODO(rFindley) consider storing TParams here, rather than FuncType, as - // they are only valid for declared functions } ) diff --git a/libgo/go/go/ast/ast_notypeparams.go b/libgo/go/go/ast/ast_notypeparams.go deleted file mode 100644 index fa132fb..0000000 --- a/libgo/go/go/ast/ast_notypeparams.go +++ /dev/null @@ -1,28 +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. - -//go:build !typeparams -// +build !typeparams - -package ast - -import "go/token" - -type ( - // A FuncType node represents a function type. - FuncType struct { - Func token.Pos // position of "func" keyword (token.NoPos if there is no "func") - Params *FieldList // (incoming) parameters; non-nil - Results *FieldList // (outgoing) results; or nil - } - - // A TypeSpec node represents a type declaration (TypeSpec production). - TypeSpec struct { - Doc *CommentGroup // associated documentation; or nil - Name *Ident // type name - Assign token.Pos // position of '=', if any - Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes - Comment *CommentGroup // line comments; or nil - } -) diff --git a/libgo/go/go/ast/ast_typeparams.go b/libgo/go/go/ast/ast_typeparams.go deleted file mode 100644 index 24fdc5f..0000000 --- a/libgo/go/go/ast/ast_typeparams.go +++ /dev/null @@ -1,51 +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. - -//go:build typeparams -// +build typeparams - -package ast - -import "go/token" - -type ( - // A FuncType node represents a function type. - FuncType struct { - Func token.Pos // position of "func" keyword (token.NoPos if there is no "func") - TParams *FieldList // type parameters; or nil - Params *FieldList // (incoming) parameters; non-nil - Results *FieldList // (outgoing) results; or nil - } - - // A TypeSpec node represents a type declaration (TypeSpec production). - TypeSpec struct { - Doc *CommentGroup // associated documentation; or nil - Name *Ident // type name - TParams *FieldList // type parameters; or nil - Assign token.Pos // position of '=', if any - Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes - Comment *CommentGroup // line comments; or nil - } - - // A ListExpr node represents a list of expressions separated by commas. - // ListExpr nodes are used as index in IndexExpr nodes representing type - // or function instantiations with more than one type argument. - ListExpr struct { - ElemList []Expr - } -) - -func (*ListExpr) exprNode() {} -func (x *ListExpr) Pos() token.Pos { - if len(x.ElemList) > 0 { - return x.ElemList[0].Pos() - } - return token.NoPos -} -func (x *ListExpr) End() token.Pos { - if len(x.ElemList) > 0 { - return x.ElemList[len(x.ElemList)-1].End() - } - return token.NoPos -} diff --git a/libgo/go/go/ast/print.go b/libgo/go/go/ast/print.go index d86d9ba..85e6943 100644 --- a/libgo/go/go/ast/print.go +++ b/libgo/go/go/ast/print.go @@ -21,7 +21,7 @@ type FieldFilter func(name string, value reflect.Value) bool // it returns false otherwise. func NotNilFilter(_ string, v reflect.Value) bool { switch v.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return !v.IsNil() } return true @@ -36,17 +36,17 @@ func NotNilFilter(_ string, v reflect.Value) bool { // struct fields for which f(fieldname, fieldvalue) is true are // printed; all others are filtered from the output. Unexported // struct fields are never printed. -func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) error { +func Fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) error { return fprint(w, fset, x, f) } -func fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) { +func fprint(w io.Writer, fset *token.FileSet, x any, f FieldFilter) (err error) { // setup printer p := printer{ output: w, fset: fset, filter: f, - ptrmap: make(map[interface{}]int), + ptrmap: make(map[any]int), last: '\n', // force printing of line number on first line } @@ -70,7 +70,7 @@ func fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err // Print prints x to standard output, skipping nil fields. // Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter). -func Print(fset *token.FileSet, x interface{}) error { +func Print(fset *token.FileSet, x any) error { return Fprint(os.Stdout, fset, x, NotNilFilter) } @@ -78,10 +78,10 @@ type printer struct { output io.Writer fset *token.FileSet filter FieldFilter - ptrmap map[interface{}]int // *T -> line number - indent int // current indentation level - last byte // the last byte processed by Write - line int // current line number + ptrmap map[any]int // *T -> line number + indent int // current indentation level + last byte // the last byte processed by Write + line int // current line number } var indent = []byte(". ") @@ -125,7 +125,7 @@ type localError struct { } // printf is a convenience wrapper that takes care of print errors. -func (p *printer) printf(format string, args ...interface{}) { +func (p *printer) printf(format string, args ...any) { if _, err := fmt.Fprintf(p, format, args...); err != nil { panic(localError{err}) } @@ -165,7 +165,7 @@ func (p *printer) print(x reflect.Value) { } p.printf("}") - case reflect.Ptr: + case reflect.Pointer: p.printf("*") // type-checked ASTs may contain cycles - use ptrmap // to keep track of objects that have been printed diff --git a/libgo/go/go/ast/print_test.go b/libgo/go/go/ast/print_test.go index 210f164..6691ccd 100644 --- a/libgo/go/go/ast/print_test.go +++ b/libgo/go/go/ast/print_test.go @@ -11,7 +11,7 @@ import ( ) var tests = []struct { - x interface{} // x is printed as s + x any // x is printed as s s string }{ // basic types diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go index c1830b5..126a27b 100644 --- a/libgo/go/go/ast/resolve.go +++ b/libgo/go/go/ast/resolve.go @@ -22,7 +22,7 @@ func (p *pkgBuilder) error(pos token.Pos, msg string) { p.errors.Add(p.fset.Position(pos), msg) } -func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) { +func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...any) { p.error(pos, fmt.Sprintf(format, args...)) } diff --git a/libgo/go/go/ast/scope.go b/libgo/go/go/ast/scope.go index a400c71..d24a5f0 100644 --- a/libgo/go/go/ast/scope.go +++ b/libgo/go/go/ast/scope.go @@ -75,10 +75,10 @@ func (s *Scope) String() string { // type Object struct { Kind ObjKind - Name string // declared name - Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil - Data interface{} // object-specific data; or nil - Type interface{} // placeholder for type information; may be nil + Name string // declared name + Decl any // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil + Data any // object-specific data; or nil + Type any // placeholder for type information; may be nil } // NewObj creates a new object of a given kind and name. diff --git a/libgo/go/go/ast/walk.go b/libgo/go/go/ast/walk.go index 9224264..308662f 100644 --- a/libgo/go/go/ast/walk.go +++ b/libgo/go/go/ast/walk.go @@ -4,6 +4,8 @@ package ast +import "fmt" + // A Visitor's Visit method is invoked for each node encountered by Walk. // If the result visitor w is not nil, Walk visits each of the children // of node with the visitor w, followed by a call of w.Visit(nil). @@ -114,6 +116,12 @@ func Walk(v Visitor, node Node) { Walk(v, n.X) Walk(v, n.Index) + case *IndexListExpr: + Walk(v, n.X) + for _, index := range n.Indices { + Walk(v, index) + } + case *SliceExpr: Walk(v, n.X) if n.Low != nil { @@ -161,7 +169,9 @@ func Walk(v Visitor, node Node) { Walk(v, n.Fields) case *FuncType: - walkFuncTypeParams(v, n) + if n.TypeParams != nil { + Walk(v, n.TypeParams) + } if n.Params != nil { Walk(v, n.Params) } @@ -316,7 +326,9 @@ func Walk(v Visitor, node Node) { Walk(v, n.Doc) } Walk(v, n.Name) - walkTypeSpecParams(v, n) + if n.TypeParams != nil { + Walk(v, n.TypeParams) + } Walk(v, n.Type) if n.Comment != nil { Walk(v, n.Comment) @@ -363,7 +375,7 @@ func Walk(v Visitor, node Node) { } default: - walkOtherNodes(v, n) + panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n)) } v.Visit(nil) diff --git a/libgo/go/go/ast/walk_notypeparams.go b/libgo/go/go/ast/walk_notypeparams.go deleted file mode 100644 index d43e13d..0000000 --- a/libgo/go/go/ast/walk_notypeparams.go +++ /dev/null @@ -1,17 +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. - -//go:build !typeparams -// +build !typeparams - -package ast - -import "fmt" - -func walkFuncTypeParams(v Visitor, n *FuncType) {} -func walkTypeSpecParams(v Visitor, n *TypeSpec) {} - -func walkOtherNodes(v Visitor, n Node) { - panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n)) -} diff --git a/libgo/go/go/ast/walk_typeparams.go b/libgo/go/go/ast/walk_typeparams.go deleted file mode 100644 index b662133..0000000 --- a/libgo/go/go/ast/walk_typeparams.go +++ /dev/null @@ -1,36 +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. - -//go:build typeparams -// +build typeparams - -package ast - -import ( - "fmt" -) - -func walkFuncTypeParams(v Visitor, n *FuncType) { - if n.TParams != nil { - Walk(v, n.TParams) - } -} - -func walkTypeSpecParams(v Visitor, n *TypeSpec) { - if n.TParams != nil { - Walk(v, n.TParams) - } -} - -func walkOtherNodes(v Visitor, n Node) { - if e, ok := n.(*ListExpr); ok { - if e != nil { - for _, elem := range e.ElemList { - Walk(v, elem) - } - } - } else { - panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n)) - } -} diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index f19f3d4..1e91e6e 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -791,7 +791,7 @@ Found: } // package was not found - return p, fmt.Errorf("cannot find package %q in:\n\t%s", path, p.Dir) + return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir) } if mode&FindOnly != 0 { @@ -896,7 +896,7 @@ Found: isTest := strings.HasSuffix(name, "_test.go") isXTest := false - if isTest && strings.HasSuffix(pkg, "_test") { + if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg { isXTest = true pkg = pkg[:len(pkg)-len("_test")] } @@ -1260,19 +1260,14 @@ func findImportComment(data []byte) (s string, line int) { var comment []byte switch { case bytes.HasPrefix(data, slashSlash): - i := bytes.Index(data, newline) - if i < 0 { - i = len(data) - } - comment = data[2:i] + comment, _, _ = bytes.Cut(data[2:], newline) case bytes.HasPrefix(data, slashStar): - data = data[2:] - i := bytes.Index(data, starSlash) - if i < 0 { + var ok bool + comment, _, ok = bytes.Cut(data[2:], starSlash) + if !ok { // malformed comment return "", 0 } - comment = data[:i] if bytes.Contains(comment, newline) { return "", 0 } @@ -1656,12 +1651,10 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) } // Split at colon. - line = strings.TrimSpace(line[4:]) - i := strings.Index(line, ":") - if i < 0 { + line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":") + if !ok { return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } - line, argstr := line[:i], line[i+1:] // Parse GOOS/GOARCH stuff. f := strings.Fields(line) @@ -1687,7 +1680,6 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) if err != nil { return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } - var ok bool for i, arg := range args { if arg, ok = expandSrcDir(arg, di.Dir); !ok { return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) @@ -1946,9 +1938,7 @@ func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { // if GOOS=illumos, then files with GOOS=solaris are also matched. // if GOOS=ios, then files with GOOS=darwin are also matched. func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { - if dot := strings.Index(name, "."); dot != -1 { - name = name[:dot] - } + name, _, _ = strings.Cut(name, ".") // Before Go 1.4, a file called "linux.go" would be equivalent to having a // build tag "linux" in that file. For Go 1.4 and beyond, we require this diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go index a8f361a..438dd47 100644 --- a/libgo/go/go/build/build_test.go +++ b/libgo/go/go/build/build_test.go @@ -714,7 +714,7 @@ func TestMissingImportErrorRepetition(t *testing.T) { // Also don't count instances in suggested "go get" or similar commands // (see https://golang.org/issue/41576). The suggested command typically // follows a semicolon. - errStr = strings.SplitN(errStr, ";", 2)[0] + errStr, _, _ = strings.Cut(errStr, ";") if n := strings.Count(errStr, pkgPath); n != 1 { t.Fatalf("package path %q appears in error %d times; should appear once\nerror: %v", pkgPath, n, err) diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go index e9b4e71..ab92b56 100644 --- a/libgo/go/go/build/deps_test.go +++ b/libgo/go/go/build/deps_test.go @@ -70,18 +70,20 @@ import ( var depsRules = ` # No dependencies allowed for any of these packages. NONE - < container/list, container/ring, - internal/cfg, internal/cpu, internal/goexperiment, + < constraints, container/list, container/ring, + internal/cfg, internal/cpu, internal/goarch, + internal/goexperiment, internal/goos, internal/goversion, internal/nettrace, unicode/utf8, unicode/utf16, unicode, unsafe; - # These packages depend only on unsafe. - unsafe + # These packages depend only on internal/goarch and unsafe. + internal/goarch, unsafe < internal/abi; # RUNTIME is the core runtime group of packages, all of them very light-weight. - internal/abi, internal/cpu, internal/goexperiment, unsafe + internal/abi, internal/cpu, internal/goarch, + internal/goexperiment, internal/goos, unsafe < internal/bytealg < internal/itoa < internal/unsafeheader @@ -171,7 +173,7 @@ var depsRules = ` io/fs < embed; - unicode, fmt !< os, os/signal; + unicode, fmt !< net, os, os/signal; os/signal, STR < path/filepath @@ -185,6 +187,8 @@ var depsRules = ` OS < golang.org/x/sys/cpu; + os < internal/godebug; + # FMT is OS (which includes string routines) plus reflect and fmt. # It does not include package log, which should be avoided in core packages. strconv, unicode @@ -212,7 +216,6 @@ var depsRules = ` mime/quotedprintable, net/internal/socktest, net/url, - runtime/debug, runtime/trace, text/scanner, text/tabwriter; @@ -269,8 +272,10 @@ var depsRules = ` # executable parsing FMT, encoding/binary, compress/zlib + < runtime/debug < debug/dwarf < debug/elf, debug/gosym, debug/macho, debug/pe, debug/plan9obj, internal/xcoff + < debug/buildinfo < DEBUG; # go parser and friends. @@ -328,7 +333,7 @@ var depsRules = ` < C < runtime/cgo < CGO - < runtime/race, runtime/msan; + < runtime/race, runtime/msan, runtime/asan; # Bulk of the standard library must not use cgo. # The prohibition stops at net and os/user. @@ -349,6 +354,13 @@ var depsRules = ` golang.org/x/net/lif, golang.org/x/net/route; + os, runtime, strconv, sync, unsafe, + internal/godebug + < internal/intern; + + internal/bytealg, internal/intern, internal/itoa, math/bits, sort, strconv + < net/netip; + # net is unavoidable when doing any networking, # so large dependencies must be kept out. # This is a long-looking list but most of these @@ -357,10 +369,12 @@ var depsRules = ` golang.org/x/net/dns/dnsmessage, golang.org/x/net/lif, golang.org/x/net/route, + internal/godebug, internal/nettrace, internal/poll, internal/singleflight, internal/race, + net/netip, os < net; @@ -393,7 +407,8 @@ var depsRules = ` < crypto/subtle < crypto/internal/subtle < crypto/elliptic/internal/fiat - < crypto/ed25519/internal/edwards25519/field + < crypto/elliptic/internal/nistec + < crypto/ed25519/internal/edwards25519/field, golang.org/x/crypto/curve25519/internal/field < crypto/ed25519/internal/edwards25519 < crypto/cipher < crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4, @@ -421,7 +436,7 @@ var depsRules = ` CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem < golang.org/x/crypto/internal/subtle < golang.org/x/crypto/chacha20 - < golang.org/x/crypto/poly1305 + < golang.org/x/crypto/internal/poly1305 < golang.org/x/crypto/chacha20poly1305 < golang.org/x/crypto/hkdf < crypto/x509/internal/macos @@ -508,10 +523,14 @@ var depsRules = ` FMT, flag, math/rand < testing/quick; - FMT, flag, runtime/debug, runtime/trace, internal/sysinfo, math/rand + FMT, DEBUG, flag, runtime/trace, internal/sysinfo, math/rand < testing; - internal/testlog, runtime/pprof, regexp + FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token, + internal/godebug, math/rand, encoding/hex, crypto/sha256 + < internal/fuzz; + + internal/fuzz, internal/testlog, runtime/pprof, regexp < testing/internal/testdeps; OS, flag, testing, internal/cfg @@ -526,6 +545,9 @@ var depsRules = ` NET, testing, math/rand < golang.org/x/net/nettest; + syscall + < os/exec/internal/fdtest; + FMT, container/heap, math/rand < internal/trace; ` @@ -615,7 +637,7 @@ func TestDependencies(t *testing.T) { } } -var buildIgnore = []byte("\n// +build ignore") +var buildIgnore = []byte("\n//go:build ignore") func findImports(pkg string) ([]string, error) { vpkg := pkg @@ -652,6 +674,9 @@ func findImports(pkg string) ([]string, error) { if err != nil { return nil, fmt.Errorf("reading %v: %v", name, err) } + if info.parsed.Name.Name == "main" { + continue + } if bytes.Contains(info.header, buildIgnore) { continue } diff --git a/libgo/go/go/build/gc.go b/libgo/go/go/build/gc.go index e16e186..434991f 100644 --- a/libgo/go/go/build/gc.go +++ b/libgo/go/go/build/gc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc package build diff --git a/libgo/go/go/build/gccgo.go b/libgo/go/go/build/gccgo.go index c8ec704..f806729 100644 --- a/libgo/go/go/build/gccgo.go +++ b/libgo/go/go/build/gccgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo -// +build gccgo package build diff --git a/libgo/go/go/build/read.go b/libgo/go/go/build/read.go index b98c793..de5c33a 100644 --- a/libgo/go/go/build/read.go +++ b/libgo/go/go/build/read.go @@ -240,6 +240,27 @@ func (r *importReader) findEmbed(first bool) bool { } } + case '\'': + startLine = false + for r.err == nil { + if r.eof { + r.syntaxError() + } + c = r.readByteNoBuf() + if c == '\\' { + r.readByteNoBuf() + if r.err != nil { + r.syntaxError() + return false + } + continue + } + if c == '\'' { + c = r.readByteNoBuf() + goto Reswitch + } + } + case '/': c = r.readByteNoBuf() switch c { @@ -516,12 +537,12 @@ func parseGoEmbed(args string, pos token.Position) ([]fileEmbed, error) { trimBytes(i) case '`': - i := strings.Index(args[1:], "`") - if i < 0 { + var ok bool + path, _, ok = strings.Cut(args[1:], "`") + if !ok { return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) } - path = args[1 : 1+i] - trimBytes(1 + i + 1) + trimBytes(1 + len(path) + 1) case '"': i := 1 diff --git a/libgo/go/go/build/read_test.go b/libgo/go/go/build/read_test.go index 1e5e1c2..6851e6b 100644 --- a/libgo/go/go/build/read_test.go +++ b/libgo/go/go/build/read_test.go @@ -119,20 +119,15 @@ var readCommentsTests = []readTest{ func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) { for i, tt := range tests { - var in, testOut string - j := strings.Index(tt.in, "ℙ") - if j < 0 { - in = tt.in - testOut = tt.in - } else { - in = tt.in[:j] + tt.in[j+len("ℙ"):] - testOut = tt.in[:j] - } - d := strings.Index(tt.in, "𝔻") - if d >= 0 { - in = in[:d] + in[d+len("𝔻"):] - testOut = testOut[d+len("𝔻"):] + beforeP, afterP, _ := strings.Cut(tt.in, "ℙ") + in := beforeP + afterP + testOut := beforeP + + if beforeD, afterD, ok := strings.Cut(beforeP, "𝔻"); ok { + in = beforeD + afterD + afterP + testOut = afterD } + r := strings.NewReader(in) buf, err := read(r) if err != nil { diff --git a/libgo/go/go/build/syslist.go b/libgo/go/go/build/syslist.go index cb7d876..1b11365 100644 --- a/libgo/go/go/build/syslist.go +++ b/libgo/go/go/build/syslist.go @@ -8,4 +8,4 @@ package build // Do not remove from this list, as these are used for go/build filename matching. const goosList = "aix android darwin dragonfly freebsd hurd illumos ios js linux nacl netbsd openbsd plan9 solaris windows zos " -const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le loong64 mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm alpha m68k nios2 sh shbe " +const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be loong64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv riscv64 s390 s390x sparc sparc64 wasm alpha m68k nios2 sh shbe " diff --git a/libgo/go/go/constant/kind_string.go b/libgo/go/go/constant/kind_string.go new file mode 100644 index 0000000..7003325 --- /dev/null +++ b/libgo/go/go/constant/kind_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type Kind"; DO NOT EDIT. + +package constant + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Unknown-0] + _ = x[Bool-1] + _ = x[String-2] + _ = x[Int-3] + _ = x[Float-4] + _ = x[Complex-5] +} + +const _Kind_name = "UnknownBoolStringIntFloatComplex" + +var _Kind_index = [...]uint8{0, 7, 11, 17, 20, 25, 32} + +func (i Kind) String() string { + if i < 0 || i >= Kind(len(_Kind_index)-1) { + return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] +} diff --git a/libgo/go/go/constant/value.go b/libgo/go/go/constant/value.go index 78cb3f8..dee3bce 100644 --- a/libgo/go/go/constant/value.go +++ b/libgo/go/go/constant/value.go @@ -24,6 +24,8 @@ import ( "unicode/utf8" ) +//go:generate stringer -type Kind + // Kind specifies the kind of value represented by a Value. type Kind int @@ -577,7 +579,7 @@ func Float64Val(x Value) (float64, bool) { // Float *big.Float or *big.Rat // everything else nil // -func Val(x Value) interface{} { +func Val(x Value) any { switch x := x.(type) { case boolVal: return bool(x) @@ -608,7 +610,7 @@ func Val(x Value) interface{} { // *big.Rat Float // anything else Unknown // -func Make(x interface{}) Value { +func Make(x any) Value { switch x := x.(type) { case bool: return boolVal(x) diff --git a/libgo/go/go/constant/value_test.go b/libgo/go/go/constant/value_test.go index 91ad0b0..e41315e 100644 --- a/libgo/go/go/constant/value_test.go +++ b/libgo/go/go/constant/value_test.go @@ -143,9 +143,9 @@ func testNumbers(t *testing.T, kind token.Token, tests []string) { if a[1] == "?" { y = MakeUnknown() } else { - if i := strings.Index(a[1], "/"); i >= 0 && kind == token.FLOAT { - n := MakeFromLiteral(a[1][:i], token.INT, 0) - d := MakeFromLiteral(a[1][i+1:], token.INT, 0) + if ns, ds, ok := strings.Cut(a[1], "/"); ok && kind == token.FLOAT { + n := MakeFromLiteral(ns, token.INT, 0) + d := MakeFromLiteral(ds, token.INT, 0) y = BinaryOp(n, token.QUO, d) } else { y = MakeFromLiteral(a[1], kind, 0) @@ -454,10 +454,10 @@ func val(lit string) Value { return MakeBool(false) } - if i := strings.IndexByte(lit, '/'); i >= 0 { + if as, bs, ok := strings.Cut(lit, "/"); ok { // assume fraction - a := MakeFromLiteral(lit[:i], token.INT, 0) - b := MakeFromLiteral(lit[i+1:], token.INT, 0) + a := MakeFromLiteral(as, token.INT, 0) + b := MakeFromLiteral(bs, token.INT, 0) return BinaryOp(a, token.QUO, b) } @@ -659,10 +659,10 @@ func TestMakeFloat64(t *testing.T) { type makeTestCase struct { kind Kind - arg, want interface{} + arg, want any } -func dup(k Kind, x interface{}) makeTestCase { return makeTestCase{k, x, x} } +func dup(k Kind, x any) makeTestCase { return makeTestCase{k, x, x} } func TestMake(t *testing.T) { for _, test := range []makeTestCase{ diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go index 92131a3..a93c05f 100644 --- a/libgo/go/go/doc/comment.go +++ b/libgo/go/go/doc/comment.go @@ -236,26 +236,24 @@ func heading(line string) string { // allow "'" for possessive "'s" only for b := line; ; { - i := strings.IndexRune(b, '\'') - if i < 0 { + var ok bool + if _, b, ok = strings.Cut(b, "'"); !ok { break } - if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') { - return "" // not followed by "s " + if b != "s" && !strings.HasPrefix(b, "s ") { + return "" // ' not followed by s and then end-of-word } - b = b[i+2:] } // allow "." when followed by non-space for b := line; ; { - i := strings.IndexRune(b, '.') - if i < 0 { + var ok bool + if _, b, ok = strings.Cut(b, "."); !ok { break } - if i+1 >= len(b) || b[i+1] == ' ' { + if b == "" || strings.HasPrefix(b, " ") { return "" // not followed by non-space } - b = b[i+1:] } return line diff --git a/libgo/go/go/doc/doc.go b/libgo/go/go/doc/doc.go index 79d3899..5ab854d 100644 --- a/libgo/go/go/doc/doc.go +++ b/libgo/go/go/doc/doc.go @@ -157,7 +157,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package { // NewFromFiles takes ownership of the AST files and may edit them, // unless the PreserveAST Mode bit is on. // -func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...interface{}) (*Package, error) { +func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...any) (*Package, error) { // Check for invalid API usage. if fset == nil { panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)")) diff --git a/libgo/go/go/doc/doc_test.go b/libgo/go/go/doc/doc_test.go index cbdca62..00cb085 100644 --- a/libgo/go/go/doc/doc_test.go +++ b/libgo/go/go/doc/doc_test.go @@ -16,6 +16,7 @@ import ( "os" "path/filepath" "regexp" + "runtime" "strings" "testing" "text/template" @@ -38,7 +39,7 @@ func readTemplate(filename string) *template.Template { return template.Must(t.ParseFiles(filepath.Join(dataDir, filename))) } -func nodeFmt(node interface{}, fset *token.FileSet) string { +func nodeFmt(node any, fset *token.FileSet) string { var buf bytes.Buffer printer.Fprint(&buf, fset, node) return strings.ReplaceAll(strings.TrimSpace(buf.String()), "\n", "\n\t") @@ -100,58 +101,56 @@ func test(t *testing.T, mode Mode) { // test packages for _, pkg := range pkgs { - importPath := dataDir + "/" + pkg.Name - var files []*ast.File - for _, f := range pkg.Files { - files = append(files, f) - } - doc, err := NewFromFiles(fset, files, importPath, mode) - if err != nil { - t.Error(err) - continue - } + t.Run(pkg.Name, func(t *testing.T) { + importPath := dataDir + "/" + pkg.Name + var files []*ast.File + for _, f := range pkg.Files { + files = append(files, f) + } + doc, err := NewFromFiles(fset, files, importPath, mode) + if err != nil { + t.Fatal(err) + } - // golden files always use / in filenames - canonicalize them - for i, filename := range doc.Filenames { - doc.Filenames[i] = filepath.ToSlash(filename) - } + // golden files always use / in filenames - canonicalize them + for i, filename := range doc.Filenames { + doc.Filenames[i] = filepath.ToSlash(filename) + } - // print documentation - var buf bytes.Buffer - if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil { - t.Error(err) - continue - } - got := buf.Bytes() + // print documentation + var buf bytes.Buffer + if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil { + t.Fatal(err) + } + got := buf.Bytes() - // update golden file if necessary - golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode)) - if *update { - err := os.WriteFile(golden, got, 0644) - if err != nil { - t.Error(err) + // update golden file if necessary + golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode)) + if *update { + err := os.WriteFile(golden, got, 0644) + if err != nil { + t.Fatal(err) + } } - continue - } - // get golden file - want, err := os.ReadFile(golden) - if err != nil { - t.Error(err) - continue - } + // get golden file + want, err := os.ReadFile(golden) + if err != nil { + t.Fatal(err) + } - // compare - if !bytes.Equal(got, want) { - t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want) - } + // compare + if !bytes.Equal(got, want) { + t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want) + } + }) } } func Test(t *testing.T) { - test(t, 0) - test(t, AllDecls) - test(t, AllMethods) + t.Run("default", func(t *testing.T) { test(t, 0) }) + t.Run("AllDecls", func(t *testing.T) { test(t, AllDecls) }) + t.Run("AllMethods", func(t *testing.T) { test(t, AllMethods) }) } func TestAnchorID(t *testing.T) { @@ -162,3 +161,153 @@ func TestAnchorID(t *testing.T) { t.Errorf("anchorID(%q) = %q; want %q", in, got, want) } } + +func TestFuncs(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("FIXME skipping for gofrontend: generics") + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "funcs.go", strings.NewReader(funcsTestFile), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + doc, err := NewFromFiles(fset, []*ast.File{file}, "importPath", Mode(0)) + if err != nil { + t.Fatal(err) + } + + for _, f := range doc.Funcs { + f.Decl = nil + } + for _, ty := range doc.Types { + for _, f := range ty.Funcs { + f.Decl = nil + } + for _, m := range ty.Methods { + m.Decl = nil + } + } + + /* FIXME: Commented out for gofrontend. + + compareFuncs := func(t *testing.T, msg string, got, want *Func) { + // ignore Decl and Examples + got.Decl = nil + got.Examples = nil + if !(got.Doc == want.Doc && + got.Name == want.Name && + got.Recv == want.Recv && + got.Orig == want.Orig && + got.Level == want.Level) { + t.Errorf("%s:\ngot %+v\nwant %+v", msg, got, want) + } + } + + compareSlices(t, "Funcs", doc.Funcs, funcsPackage.Funcs, compareFuncs) + compareSlices(t, "Types", doc.Types, funcsPackage.Types, func(t *testing.T, msg string, got, want *Type) { + if got.Name != want.Name { + t.Errorf("%s.Name: got %q, want %q", msg, got.Name, want.Name) + } else { + compareSlices(t, got.Name+".Funcs", got.Funcs, want.Funcs, compareFuncs) + compareSlices(t, got.Name+".Methods", got.Methods, want.Methods, compareFuncs) + } + }) + */ +} + +/* FIXME: Commented out for gofrontend. + +func compareSlices[E any](t *testing.T, name string, got, want []E, compareElem func(*testing.T, string, E, E)) { + if len(got) != len(want) { + t.Errorf("%s: got %d, want %d", name, len(got), len(want)) + } + for i := 0; i < len(got) && i < len(want); i++ { + compareElem(t, fmt.Sprintf("%s[%d]", name, i), got[i], want[i]) + } +} + +*/ + +const funcsTestFile = ` +package funcs + +func F() {} + +type S1 struct { + S2 // embedded, exported + s3 // embedded, unexported +} + +func NewS1() S1 {return S1{} } +func NewS1p() *S1 { return &S1{} } + +func (S1) M1() {} +func (r S1) M2() {} +func(S1) m3() {} // unexported not shown +func (*S1) P1() {} // pointer receiver + +type S2 int +func (S2) M3() {} // shown on S2 + +type s3 int +func (s3) M4() {} // shown on S1 + +type G1[T any] struct { + *s3 +} + +func NewG1[T any]() G1[T] { return G1[T]{} } + +func (G1[T]) MG1() {} +func (*G1[U]) MG2() {} + +type G2[T, U any] struct {} + +func NewG2[T, U any]() G2[T, U] { return G2[T, U]{} } + +func (G2[T, U]) MG3() {} +func (*G2[A, B]) MG4() {} + + +` + +var funcsPackage = &Package{ + Funcs: []*Func{{Name: "F"}}, + Types: []*Type{ + { + Name: "G1", + Funcs: []*Func{{Name: "NewG1"}}, + Methods: []*Func{ + {Name: "M4", Recv: "G1", // TODO: synthesize a param for G1? + Orig: "s3", Level: 1}, + {Name: "MG1", Recv: "G1[T]", Orig: "G1[T]", Level: 0}, + {Name: "MG2", Recv: "*G1[U]", Orig: "*G1[U]", Level: 0}, + }, + }, + { + Name: "G2", + Funcs: []*Func{{Name: "NewG2"}}, + Methods: []*Func{ + {Name: "MG3", Recv: "G2[T, U]", Orig: "G2[T, U]", Level: 0}, + {Name: "MG4", Recv: "*G2[A, B]", Orig: "*G2[A, B]", Level: 0}, + }, + }, + { + Name: "S1", + Funcs: []*Func{{Name: "NewS1"}, {Name: "NewS1p"}}, + Methods: []*Func{ + {Name: "M1", Recv: "S1", Orig: "S1", Level: 0}, + {Name: "M2", Recv: "S1", Orig: "S1", Level: 0}, + {Name: "M4", Recv: "S1", Orig: "s3", Level: 1}, + {Name: "P1", Recv: "*S1", Orig: "*S1", Level: 0}, + }, + }, + { + Name: "S2", + Methods: []*Func{ + {Name: "M3", Recv: "S2", Orig: "S2", Level: 0}, + }, + }, + }, +} diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go index 274000c..0a880cd 100644 --- a/libgo/go/go/doc/example.go +++ b/libgo/go/go/doc/example.go @@ -44,13 +44,13 @@ type Example struct { // identifiers from other packages (or predeclared identifiers, such as // "int") and the test file does not include a dot import. // - The entire test file is the example: the file contains exactly one -// example function, zero test or benchmark functions, and at least one -// top-level function, type, variable, or constant declaration other -// than the example function. +// example function, zero test, fuzz test, or benchmark function, and at +// least one top-level function, type, variable, or constant declaration +// other than the example function. func Examples(testFiles ...*ast.File) []*Example { var list []*Example for _, file := range testFiles { - hasTests := false // file contains tests or benchmarks + hasTests := false // file contains tests, fuzz test, or benchmarks numDecl := 0 // number of non-import declarations in the file var flist []*Example for _, decl := range file.Decls { @@ -64,7 +64,7 @@ func Examples(testFiles ...*ast.File) []*Example { } numDecl++ name := f.Name.Name - if isTest(name, "Test") || isTest(name, "Benchmark") { + if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Fuzz") { hasTests = true continue } @@ -133,9 +133,9 @@ func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output strin return "", false, false // no suitable comment found } -// isTest tells whether name looks like a test, example, or benchmark. -// It is a Test (say) if there is a character after Test that is not a -// lower-case letter. (We don't want Testiness.) +// isTest tells whether name looks like a test, example, fuzz test, or +// benchmark. It is a Test (say) if there is a character after Test that is not +// a lower-case letter. (We don't want Testiness.) func isTest(name, prefix string) bool { if !strings.HasPrefix(name, prefix) { return false diff --git a/libgo/go/go/doc/example_test.go b/libgo/go/go/doc/example_test.go index cf1b702..21b7129 100644 --- a/libgo/go/go/doc/example_test.go +++ b/libgo/go/go/doc/example_test.go @@ -307,6 +307,9 @@ func (X) TestBlah() { func (X) BenchmarkFoo() { } +func (X) FuzzFoo() { +} + func Example() { fmt.Println("Hello, world!") // Output: Hello, world! @@ -326,6 +329,9 @@ func (X) TestBlah() { func (X) BenchmarkFoo() { } +func (X) FuzzFoo() { +} + func main() { fmt.Println("Hello, world!") } diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go index 819c030..671c622 100644 --- a/libgo/go/go/doc/exports.go +++ b/libgo/go/go/doc/exports.go @@ -79,18 +79,15 @@ func hasExportedName(list []*ast.Ident) bool { return false } -// removeErrorField removes anonymous fields named "error" from an interface. -// This is called when "error" has been determined to be a local name, -// not the predeclared type. -// -func removeErrorField(ityp *ast.InterfaceType) { +// removeAnonymousField removes anonymous fields named name from an interface. +func removeAnonymousField(name string, ityp *ast.InterfaceType) { list := ityp.Methods.List // we know that ityp.Methods != nil j := 0 for _, field := range list { keepField := true if n := len(field.Names); n == 0 { // anonymous field - if fname, _ := baseTypeName(field.Type); fname == "error" { + if fname, _ := baseTypeName(field.Type); fname == name { keepField = false } } @@ -119,16 +116,25 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp for _, field := range list { keepField := false if n := len(field.Names); n == 0 { - // anonymous field + // anonymous field or embedded type or union element fname := r.recordAnonymousField(parent, field.Type) - if token.IsExported(fname) { - keepField = true - } else if ityp != nil && fname == "error" { - // possibly the predeclared error interface; keep - // it for now but remember this interface so that - // it can be fixed if error is also defined locally - keepField = true - r.remember(ityp) + if fname != "" { + if token.IsExported(fname) { + keepField = true + } else if ityp != nil && predeclaredTypes[fname] { + // possibly an embedded predeclared type; keep it for now but + // remember this interface so that it can be fixed if name is also + // defined locally + keepField = true + r.remember(fname, ityp) + } + } else { + // If we're operating on an interface, assume that this is an embedded + // type or union element. + // + // TODO(rfindley): consider traversing into approximation/unions + // elements to see if they are entirely unexported. + keepField = ityp != nil } } else { field.Names = filterIdentList(field.Names) @@ -172,6 +178,17 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) { // nothing to do case *ast.ParenExpr: r.filterType(nil, t.X) + case *ast.StarExpr: // possibly an embedded type literal + r.filterType(nil, t.X) + case *ast.UnaryExpr: + if t.Op == token.TILDE { // approximation element + r.filterType(nil, t.X) + } + case *ast.BinaryExpr: + if t.Op == token.OR { // union + r.filterType(nil, t.X) + r.filterType(nil, t.Y) + } case *ast.ArrayType: r.filterType(nil, t.Elt) case *ast.StructType: @@ -179,6 +196,7 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) { t.Incomplete = true } case *ast.FuncType: + r.filterParamList(t.TypeParams) r.filterParamList(t.Params) r.filterParamList(t.Results) case *ast.InterfaceType: @@ -219,12 +237,16 @@ func (r *reader) filterSpec(spec ast.Spec) bool { } } case *ast.TypeSpec: + // Don't filter type parameters here, by analogy with function parameters + // which are not filtered for top-level function declarations. if name := s.Name.Name; token.IsExported(name) { r.filterType(r.lookupType(s.Name.Name), s.Type) return true - } else if name == "error" { - // special case: remember that error is declared locally - r.errorDecl = true + } else if IsPredeclared(name) { + if r.shadowedPredecl == nil { + r.shadowedPredecl = make(map[string]bool) + } + r.shadowedPredecl[name] = true } } return false diff --git a/libgo/go/go/doc/filter.go b/libgo/go/go/doc/filter.go index a6f243f..9904da1 100644 --- a/libgo/go/go/doc/filter.go +++ b/libgo/go/go/doc/filter.go @@ -34,6 +34,8 @@ func matchDecl(d *ast.GenDecl, f Filter) bool { if f(v.Name.Name) { return true } + // We don't match ordinary parameters in filterFuncs, so by analogy don't + // match type parameters here. switch t := v.Type.(type) { case *ast.StructType: if matchFields(t.Fields, f) { diff --git a/libgo/go/go/doc/headscan.go b/libgo/go/go/doc/headscan.go index 28cb84f..320895e 100644 --- a/libgo/go/go/doc/headscan.go +++ b/libgo/go/go/doc/headscan.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* The headscan command extracts comment headings from package files; @@ -23,10 +22,10 @@ import ( "go/doc" "go/parser" "go/token" - "internal/lazyregexp" "io/fs" "os" "path/filepath" + "regexp" "runtime" "strings" ) @@ -37,7 +36,7 @@ var ( ) // ToHTML in comment.go assigns a (possibly blank) ID to each heading -var html_h = lazyregexp.New(`<h3 id="[^"]*">`) +var html_h = regexp.MustCompile(`<h3 id="[^"]*">`) const html_endh = "</h3>\n" @@ -49,19 +48,14 @@ func isGoFile(fi fs.FileInfo) bool { func appendHeadings(list []string, comment string) []string { var buf bytes.Buffer doc.ToHTML(&buf, comment, nil) - for s := buf.String(); ; { + for s := buf.String(); s != ""; { loc := html_h.FindStringIndex(s) if len(loc) == 0 { break } - i := loc[1] - j := strings.Index(s, html_endh) - if j < 0 { - list = append(list, s[i:]) // incorrect HTML - break - } - list = append(list, s[i:j]) - s = s[j+len(html_endh):] + var inner string + inner, s, _ = strings.Cut(s[loc[1]:], html_endh) + list = append(list, inner) } return list } diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go index c277b35..de1d422 100644 --- a/libgo/go/go/doc/reader.go +++ b/libgo/go/go/doc/reader.go @@ -5,11 +5,13 @@ package doc import ( + "fmt" "go/ast" "go/token" "internal/lazyregexp" "sort" "strconv" + "strings" ) // ---------------------------------------------------------------------------- @@ -22,8 +24,8 @@ import ( // type methodSet map[string]*Func -// recvString returns a string representation of recv of the -// form "T", "*T", or "BADRECV" (if not a proper receiver type). +// recvString returns a string representation of recv of the form "T", "*T", +// "T[A, ...]", "*T[A, ...]" or "BADRECV" (if not a proper receiver type). // func recvString(recv ast.Expr) string { switch t := recv.(type) { @@ -31,10 +33,34 @@ func recvString(recv ast.Expr) string { return t.Name case *ast.StarExpr: return "*" + recvString(t.X) + case *ast.IndexExpr: + // Generic type with one parameter. + return fmt.Sprintf("%s[%s]", recvString(t.X), recvParam(t.Index)) + case *ast.IndexListExpr: + // Generic type with multiple parameters. + if len(t.Indices) > 0 { + var b strings.Builder + b.WriteString(recvString(t.X)) + b.WriteByte('[') + b.WriteString(recvParam(t.Indices[0])) + for _, e := range t.Indices[1:] { + b.WriteString(", ") + b.WriteString(recvParam(e)) + } + b.WriteByte(']') + return b.String() + } } return "BADRECV" } +func recvParam(p ast.Expr) string { + if id, ok := p.(*ast.Ident); ok { + return id.Name + } + return "BADPARAM" +} + // set creates the corresponding Func for f and adds it to mset. // If there are multiple f's with the same name, set keeps the first // one with documentation; conflicts are ignored. The boolean @@ -101,6 +127,10 @@ func baseTypeName(x ast.Expr) (name string, imported bool) { switch t := x.(type) { case *ast.Ident: return t.Name, false + case *ast.IndexExpr: + return baseTypeName(t.X) + case *ast.IndexListExpr: + return baseTypeName(t.X) case *ast.SelectorExpr: if _, ok := t.X.(*ast.Ident); ok { // only possible for qualified type names; @@ -112,7 +142,7 @@ func baseTypeName(x ast.Expr) (name string, imported bool) { case *ast.StarExpr: return baseTypeName(t.X) } - return + return "", false } // An embeddedSet describes a set of embedded types. @@ -163,9 +193,9 @@ type reader struct { types map[string]*namedType funcs methodSet - // support for package-local error type declarations - errorDecl bool // if set, type "error" was declared locally - fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error" + // support for package-local shadowing of predeclared types + shadowedPredecl map[string]bool + fixmap map[string][]*ast.InterfaceType } func (r *reader) isVisible(name string) bool { @@ -224,8 +254,11 @@ func (r *reader) readDoc(comment *ast.CommentGroup) { r.doc += "\n" + text } -func (r *reader) remember(typ *ast.InterfaceType) { - r.fixlist = append(r.fixlist, typ) +func (r *reader) remember(predecl string, typ *ast.InterfaceType) { + if r.fixmap == nil { + r.fixmap = make(map[string][]*ast.InterfaceType) + } + r.fixmap[predecl] = append(r.fixmap[predecl], typ) } func specNames(specs []ast.Spec) []string { @@ -418,6 +451,11 @@ func (r *reader) readFunc(fun *ast.FuncDecl) { factoryType = t.Elt } if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) { + if lookupTypeParam(n, fun.Type.TypeParams) != nil { + // Issue #49477: don't associate fun with its type parameter result. + // A type parameter is not a defined type. + continue + } if t := r.lookupType(n); t != nil { typ = t numResultTypes++ @@ -439,6 +477,22 @@ func (r *reader) readFunc(fun *ast.FuncDecl) { r.funcs.set(fun, r.mode&PreserveAST != 0) } +// lookupTypeParam searches for type parameters named name within the tparams +// field list, returning the relevant identifier if found, or nil if not. +func lookupTypeParam(name string, tparams *ast.FieldList) *ast.Ident { + if tparams == nil { + return nil + } + for _, field := range tparams.List { + for _, id := range field.Names { + if id.Name == name { + return id + } + } + } + return nil +} + var ( noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker) // MARKER(uid) at text start @@ -679,10 +733,11 @@ func (r *reader) computeMethodSets() { } } - // if error was declared locally, don't treat it as exported field anymore - if r.errorDecl { - for _, ityp := range r.fixlist { - removeErrorField(ityp) + // For any predeclared names that are declared locally, don't treat them as + // exported fields anymore. + for predecl := range r.shadowedPredecl { + for _, ityp := range r.fixmap[predecl] { + removeAnonymousField(predecl, ityp) } } } @@ -869,6 +924,7 @@ func IsPredeclared(s string) bool { } var predeclaredTypes = map[string]bool{ + "any": true, "bool": true, "byte": true, "complex64": true, diff --git a/libgo/go/go/doc/testdata/benchmark.go b/libgo/go/go/doc/testdata/benchmark.go index 1d581f0..d27bf11 100644 --- a/libgo/go/go/doc/testdata/benchmark.go +++ b/libgo/go/go/doc/testdata/benchmark.go @@ -232,7 +232,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ runtime.GOMAXPROCS(procs) b := &B{ common: common{ - signal: make(chan interface{}), + signal: make(chan any), }, benchmark: Benchmark, } @@ -285,7 +285,7 @@ func (b *B) trimOutput() { func Benchmark(f func(b *B)) BenchmarkResult { b := &B{ common: common{ - signal: make(chan interface{}), + signal: make(chan any), }, benchmark: InternalBenchmark{"", f}, } diff --git a/libgo/go/go/doc/testdata/generics.0.golden b/libgo/go/go/doc/testdata/generics.0.golden new file mode 100644 index 0000000..91c874c --- /dev/null +++ b/libgo/go/go/doc/testdata/generics.0.golden @@ -0,0 +1,76 @@ +// Package generics contains the new syntax supporting generic ... +PACKAGE generics + +IMPORTPATH + testdata/generics + +FILENAMES + testdata/generics.go + +FUNCTIONS + // AnotherFunc has an implicit constraint interface. Neither type ... + func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) + + // Func has an instantiated constraint. + func Func[T Constraint[string, Type[int]]]() + + // Single is not a factory function. + func Single[T any]() *T + + // Slice is not a factory function. + func Slice[T any]() []T + + +TYPES + // AFuncType demonstrates filtering of parameters and type ... + type AFuncType[T ~struct{ f int }] func(_ struct { + // contains filtered or unexported fields + }) + + // Constraint is a constraint interface with two type parameters. + type Constraint[P, Q interface{ string | ~int | Type[int] }] interface { + ~int | ~byte | Type[string] + M() P + } + + // NewEmbeddings demonstrates how we filter the new embedded ... + type NewEmbeddings interface { + string // should not be filtered + + struct { + // contains filtered or unexported fields + } + ~struct { + // contains filtered or unexported fields + } + *struct { + // contains filtered or unexported fields + } + struct { + // contains filtered or unexported fields + } | ~struct { + // contains filtered or unexported fields + } + // contains filtered or unexported methods + } + + // Parameterized types should be shown. + type Type[P any] struct { + Field P + } + + // Variables with an instantiated type should be shown. + var X Type[int] + + // Constructors for parameterized types should be shown. + func Constructor[lowerCase any]() Type[lowerCase] + + // MethodA uses a different name for its receiver type parameter. + func (t Type[A]) MethodA(p A) + + // MethodB has a blank receiver type parameter. + func (t Type[_]) MethodB() + + // MethodC has a lower-case receiver type parameter. + func (t Type[c]) MethodC() + diff --git a/libgo/go/go/doc/testdata/generics.1.golden b/libgo/go/go/doc/testdata/generics.1.golden new file mode 100644 index 0000000..923a4ce --- /dev/null +++ b/libgo/go/go/doc/testdata/generics.1.golden @@ -0,0 +1,66 @@ +// Package generics contains the new syntax supporting generic ... +PACKAGE generics + +IMPORTPATH + testdata/generics + +FILENAMES + testdata/generics.go + +FUNCTIONS + // AnotherFunc has an implicit constraint interface. Neither type ... + func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) + + // Func has an instantiated constraint. + func Func[T Constraint[string, Type[int]]]() + + // Single is not a factory function. + func Single[T any]() *T + + // Slice is not a factory function. + func Slice[T any]() []T + + +TYPES + // AFuncType demonstrates filtering of parameters and type ... + type AFuncType[T ~struct{ f int }] func(_ struct{ f int }) + + // Constraint is a constraint interface with two type parameters. + type Constraint[P, Q interface{ string | ~int | Type[int] }] interface { + ~int | ~byte | Type[string] + M() P + } + + // NewEmbeddings demonstrates how we filter the new embedded ... + type NewEmbeddings interface { + string // should not be filtered + int16 + struct{ f int } + ~struct{ f int } + *struct{ f int } + struct{ f int } | ~struct{ f int } + } + + // Parameterized types should be shown. + type Type[P any] struct { + Field P + } + + // Variables with an instantiated type should be shown. + var X Type[int] + + // Constructors for parameterized types should be shown. + func Constructor[lowerCase any]() Type[lowerCase] + + // MethodA uses a different name for its receiver type parameter. + func (t Type[A]) MethodA(p A) + + // MethodB has a blank receiver type parameter. + func (t Type[_]) MethodB() + + // MethodC has a lower-case receiver type parameter. + func (t Type[c]) MethodC() + + // int16 shadows the predeclared type int16. + type int16 int + diff --git a/libgo/go/go/doc/testdata/generics.2.golden b/libgo/go/go/doc/testdata/generics.2.golden new file mode 100644 index 0000000..91c874c --- /dev/null +++ b/libgo/go/go/doc/testdata/generics.2.golden @@ -0,0 +1,76 @@ +// Package generics contains the new syntax supporting generic ... +PACKAGE generics + +IMPORTPATH + testdata/generics + +FILENAMES + testdata/generics.go + +FUNCTIONS + // AnotherFunc has an implicit constraint interface. Neither type ... + func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) + + // Func has an instantiated constraint. + func Func[T Constraint[string, Type[int]]]() + + // Single is not a factory function. + func Single[T any]() *T + + // Slice is not a factory function. + func Slice[T any]() []T + + +TYPES + // AFuncType demonstrates filtering of parameters and type ... + type AFuncType[T ~struct{ f int }] func(_ struct { + // contains filtered or unexported fields + }) + + // Constraint is a constraint interface with two type parameters. + type Constraint[P, Q interface{ string | ~int | Type[int] }] interface { + ~int | ~byte | Type[string] + M() P + } + + // NewEmbeddings demonstrates how we filter the new embedded ... + type NewEmbeddings interface { + string // should not be filtered + + struct { + // contains filtered or unexported fields + } + ~struct { + // contains filtered or unexported fields + } + *struct { + // contains filtered or unexported fields + } + struct { + // contains filtered or unexported fields + } | ~struct { + // contains filtered or unexported fields + } + // contains filtered or unexported methods + } + + // Parameterized types should be shown. + type Type[P any] struct { + Field P + } + + // Variables with an instantiated type should be shown. + var X Type[int] + + // Constructors for parameterized types should be shown. + func Constructor[lowerCase any]() Type[lowerCase] + + // MethodA uses a different name for its receiver type parameter. + func (t Type[A]) MethodA(p A) + + // MethodB has a blank receiver type parameter. + func (t Type[_]) MethodB() + + // MethodC has a lower-case receiver type parameter. + func (t Type[c]) MethodC() + diff --git a/libgo/go/go/doc/testdata/generics.go b/libgo/go/go/doc/testdata/generics.go new file mode 100644 index 0000000..ba7187e --- /dev/null +++ b/libgo/go/go/doc/testdata/generics.go @@ -0,0 +1,74 @@ +// 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 generics contains the new syntax supporting generic programming in +// Go. +package generics + +// Variables with an instantiated type should be shown. +var X Type[int] + +// Parameterized types should be shown. +type Type[P any] struct { + Field P +} + +// Constructors for parameterized types should be shown. +func Constructor[lowerCase any]() Type[lowerCase] { + return Type[lowerCase]{} +} + +// MethodA uses a different name for its receiver type parameter. +func (t Type[A]) MethodA(p A) {} + +// MethodB has a blank receiver type parameter. +func (t Type[_]) MethodB() {} + +// MethodC has a lower-case receiver type parameter. +func (t Type[c]) MethodC() {} + +// Constraint is a constraint interface with two type parameters. +type Constraint[P, Q interface{ string | ~int | Type[int] }] interface { + ~int | ~byte | Type[string] + M() P +} + +// int16 shadows the predeclared type int16. +type int16 int + +// NewEmbeddings demonstrates how we filter the new embedded elements. +type NewEmbeddings interface { + string // should not be filtered + int16 + struct{ f int } + ~struct{ f int } + *struct{ f int } + struct{ f int } | ~struct{ f int } +} + +// Func has an instantiated constraint. +func Func[T Constraint[string, Type[int]]]() {} + +// AnotherFunc has an implicit constraint interface. +// +// Neither type parameters nor regular parameters should be filtered. +func AnotherFunc[T ~struct{ f int }](_ struct{ f int }) {} + +// AFuncType demonstrates filtering of parameters and type parameters. Here we +// don't filter type parameters (to be consistent with function declarations), +// but DO filter the RHS. +type AFuncType[T ~struct{ f int }] func(_ struct{ f int }) + +// See issue #49477: type parameters should not be interpreted as named types +// for the purpose of determining whether a function is a factory function. + +// Slice is not a factory function. +func Slice[T any]() []T { + return nil +} + +// Single is not a factory function. +func Single[T any]() *T { + return nil +} diff --git a/libgo/go/go/doc/testdata/testing.0.golden b/libgo/go/go/doc/testdata/testing.0.golden index 83cf37c..61dac8b 100644 --- a/libgo/go/go/doc/testdata/testing.0.golden +++ b/libgo/go/go/doc/testdata/testing.0.golden @@ -46,10 +46,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *B) Error(args ...interface{}) + func (c *B) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *B) Errorf(format string, args ...interface{}) + func (c *B) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *B) Fail() @@ -61,16 +61,16 @@ TYPES func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *B) Fatal(args ...interface{}) + func (c *B) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *B) Fatalf(format string, args ...interface{}) + func (c *B) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *B) Log(args ...interface{}) + func (c *B) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *B) Logf(format string, args ...interface{}) + func (c *B) Logf(format string, args ...any) // ResetTimer sets the elapsed benchmark time to zero. It does not ... func (b *B) ResetTimer() @@ -125,10 +125,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *T) Error(args ...interface{}) + func (c *T) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *T) Errorf(format string, args ...interface{}) + func (c *T) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *T) Fail() @@ -140,16 +140,16 @@ TYPES func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *T) Fatal(args ...interface{}) + func (c *T) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *T) Fatalf(format string, args ...interface{}) + func (c *T) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *T) Log(args ...interface{}) + func (c *T) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *T) Logf(format string, args ...interface{}) + func (c *T) Logf(format string, args ...any) // Parallel signals that this test is to be run in parallel with ... func (t *T) Parallel() diff --git a/libgo/go/go/doc/testdata/testing.1.golden b/libgo/go/go/doc/testdata/testing.1.golden index b9d1451..1655af1 100644 --- a/libgo/go/go/doc/testdata/testing.1.golden +++ b/libgo/go/go/doc/testdata/testing.1.golden @@ -119,10 +119,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *B) Error(args ...interface{}) + func (c *B) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *B) Errorf(format string, args ...interface{}) + func (c *B) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *B) Fail() @@ -134,16 +134,16 @@ TYPES func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *B) Fatal(args ...interface{}) + func (c *B) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *B) Fatalf(format string, args ...interface{}) + func (c *B) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *B) Log(args ...interface{}) + func (c *B) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *B) Logf(format string, args ...interface{}) + func (c *B) Logf(format string, args ...any) // ResetTimer sets the elapsed benchmark time to zero. It does not ... func (b *B) ResetTimer() @@ -221,10 +221,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *T) Error(args ...interface{}) + func (c *T) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *T) Errorf(format string, args ...interface{}) + func (c *T) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *T) Fail() @@ -236,16 +236,16 @@ TYPES func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *T) Fatal(args ...interface{}) + func (c *T) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *T) Fatalf(format string, args ...interface{}) + func (c *T) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *T) Log(args ...interface{}) + func (c *T) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *T) Logf(format string, args ...interface{}) + func (c *T) Logf(format string, args ...any) // Parallel signals that this test is to be run in parallel with ... func (t *T) Parallel() @@ -262,15 +262,15 @@ TYPES failed bool // Test or benchmark has failed. start time.Time // Time test or benchmark started duration time.Duration - self interface{} // To be sent on signal channel when done. - signal chan interface{} // Output for serial tests. + self any // To be sent on signal channel when done. + signal chan any // Output for serial tests. } // Error is equivalent to Log() followed by Fail(). - func (c *common) Error(args ...interface{}) + func (c *common) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *common) Errorf(format string, args ...interface{}) + func (c *common) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *common) Fail() @@ -282,16 +282,16 @@ TYPES func (c *common) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *common) Fatal(args ...interface{}) + func (c *common) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *common) Fatalf(format string, args ...interface{}) + func (c *common) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *common) Log(args ...interface{}) + func (c *common) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *common) Logf(format string, args ...interface{}) + func (c *common) Logf(format string, args ...any) // log generates the output. It's always at the same stack depth. func (c *common) log(s string) diff --git a/libgo/go/go/doc/testdata/testing.2.golden b/libgo/go/go/doc/testdata/testing.2.golden index 83cf37c..61dac8b 100644 --- a/libgo/go/go/doc/testdata/testing.2.golden +++ b/libgo/go/go/doc/testdata/testing.2.golden @@ -46,10 +46,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *B) Error(args ...interface{}) + func (c *B) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *B) Errorf(format string, args ...interface{}) + func (c *B) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *B) Fail() @@ -61,16 +61,16 @@ TYPES func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *B) Fatal(args ...interface{}) + func (c *B) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *B) Fatalf(format string, args ...interface{}) + func (c *B) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *B) Log(args ...interface{}) + func (c *B) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *B) Logf(format string, args ...interface{}) + func (c *B) Logf(format string, args ...any) // ResetTimer sets the elapsed benchmark time to zero. It does not ... func (b *B) ResetTimer() @@ -125,10 +125,10 @@ TYPES } // Error is equivalent to Log() followed by Fail(). - func (c *T) Error(args ...interface{}) + func (c *T) Error(args ...any) // Errorf is equivalent to Logf() followed by Fail(). - func (c *T) Errorf(format string, args ...interface{}) + func (c *T) Errorf(format string, args ...any) // Fail marks the function as having failed but continues ... func (c *T) Fail() @@ -140,16 +140,16 @@ TYPES func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). - func (c *T) Fatal(args ...interface{}) + func (c *T) Fatal(args ...any) // Fatalf is equivalent to Logf() followed by FailNow(). - func (c *T) Fatalf(format string, args ...interface{}) + func (c *T) Fatalf(format string, args ...any) // Log formats its arguments using default formatting, analogous ... - func (c *T) Log(args ...interface{}) + func (c *T) Log(args ...any) // Logf formats its arguments according to the format, analogous ... - func (c *T) Logf(format string, args ...interface{}) + func (c *T) Logf(format string, args ...any) // Parallel signals that this test is to be run in parallel with ... func (t *T) Parallel() diff --git a/libgo/go/go/doc/testdata/testing.go b/libgo/go/go/doc/testdata/testing.go index 52810f7..80238df 100644 --- a/libgo/go/go/doc/testdata/testing.go +++ b/libgo/go/go/doc/testdata/testing.go @@ -77,8 +77,8 @@ type common struct { failed bool // Test or benchmark has failed. start time.Time // Time test or benchmark started duration time.Duration - self interface{} // To be sent on signal channel when done. - signal chan interface{} // Output for serial tests. + self any // To be sent on signal channel when done. + signal chan any // Output for serial tests. } // Short reports whether the -test.short flag is set. @@ -167,32 +167,32 @@ func (c *common) log(s string) { // Log formats its arguments using default formatting, analogous to Println(), // and records the text in the error log. -func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) } +func (c *common) Log(args ...any) { c.log(fmt.Sprintln(args...)) } // Logf formats its arguments according to the format, analogous to Printf(), // and records the text in the error log. -func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) } +func (c *common) Logf(format string, args ...any) { c.log(fmt.Sprintf(format, args...)) } // Error is equivalent to Log() followed by Fail(). -func (c *common) Error(args ...interface{}) { +func (c *common) Error(args ...any) { c.log(fmt.Sprintln(args...)) c.Fail() } // Errorf is equivalent to Logf() followed by Fail(). -func (c *common) Errorf(format string, args ...interface{}) { +func (c *common) Errorf(format string, args ...any) { c.log(fmt.Sprintf(format, args...)) c.Fail() } // Fatal is equivalent to Log() followed by FailNow(). -func (c *common) Fatal(args ...interface{}) { +func (c *common) Fatal(args ...any) { c.log(fmt.Sprintln(args...)) c.FailNow() } // Fatalf is equivalent to Logf() followed by FailNow(). -func (c *common) Fatalf(format string, args ...interface{}) { +func (c *common) Fatalf(format string, args ...any) { c.log(fmt.Sprintf(format, args...)) c.FailNow() } @@ -269,7 +269,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT // If all tests pump to the same channel, a bug can occur where a test // kicks off a goroutine that Fails, yet the test still delivers a completion signal, // which skews the counting. - var collector = make(chan interface{}) + var collector = make(chan any) numParallel := 0 startParallel := make(chan bool) @@ -289,7 +289,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT } t := &T{ common: common{ - signal: make(chan interface{}), + signal: make(chan any), }, name: testName, startParallel: startParallel, diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go index a603d96..ea8dd20 100644 --- a/libgo/go/go/format/format.go +++ b/libgo/go/go/format/format.go @@ -51,7 +51,7 @@ const parserMode = parser.ParseComments // The function may return early (before the entire result is written) // and return a formatting error, for instance due to an incorrect AST. // -func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { +func Node(dst io.Writer, fset *token.FileSet, node any) error { // Determine if we have a complete source file (file != nil). var file *ast.File var cnode *printer.CommentedNode diff --git a/libgo/go/go/importer/importer_test.go b/libgo/go/go/importer/importer_test.go index 0f5121d..27c4aa7 100644 --- a/libgo/go/go/importer/importer_test.go +++ b/libgo/go/go/importer/importer_test.go @@ -24,8 +24,7 @@ func TestForCompiler(t *testing.T) { t.Fatalf("go list %s: %v\n%s", thePackage, err, out) } target := strings.TrimSpace(string(out)) - i := strings.Index(target, ":") - compiler, target := target[:i], target[i+1:] + compiler, target, _ := strings.Cut(target, ":") if !strings.HasSuffix(target, ".a") { t.Fatalf("unexpected package %s target %q (not *.a)", thePackage, target) } diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go index 1b1d07d..48335fa 100644 --- a/libgo/go/go/internal/gccgoimporter/parser.go +++ b/libgo/go/go/internal/gccgoimporter/parser.go @@ -80,7 +80,7 @@ func (e importError) Error() string { return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) } -func (p *parser) error(err interface{}) { +func (p *parser) error(err any) { if s, ok := err.(string); ok { err = errors.New(s) } @@ -88,7 +88,7 @@ func (p *parser) error(err interface{}) { panic(importError{p.scanner.Pos(), err.(error)}) } -func (p *parser) errorf(format string, args ...interface{}) { +func (p *parser) errorf(format string, args ...any) { p.error(fmt.Errorf(format, args...)) } @@ -474,7 +474,7 @@ func (p *parser) reserve(n int) { // used to resolve named types, or it can be a *types.Pointer, // used to resolve pointers to named types in case they are referenced // by embedded fields. -func (p *parser) update(t types.Type, nlist []interface{}) { +func (p *parser) update(t types.Type, nlist []any) { if t == reserved { p.errorf("internal error: update(%v) invoked on reserved", nlist) } @@ -509,7 +509,7 @@ func (p *parser) update(t types.Type, nlist []interface{}) { // NamedType = TypeName [ "=" ] Type { Method } . // TypeName = ExportedName . // Method = "func" "(" Param ")" Name ParamList ResultList [InlineBody] ";" . -func (p *parser) parseNamedType(nlist []interface{}) types.Type { +func (p *parser) parseNamedType(nlist []any) types.Type { pkg, name := p.parseExportedName() scope := pkg.Scope() obj := scope.Lookup(name) @@ -599,7 +599,7 @@ func (p *parser) parseNamedType(nlist []interface{}) types.Type { p.skipInlineBody() p.expectEOL() - sig := types.NewSignature(receiver, params, results, isVariadic) + sig := types.NewSignatureType(receiver, nil, nil, params, results, isVariadic) nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) } } @@ -626,7 +626,7 @@ func (p *parser) parseInt() int { } // ArrayOrSliceType = "[" [ int ] "]" Type . -func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []any) types.Type { p.expect('[') if p.tok == ']' { p.next() @@ -649,7 +649,7 @@ func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []interface{}) } // MapType = "map" "[" Type "]" Type . -func (p *parser) parseMapType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseMapType(pkg *types.Package, nlist []any) types.Type { p.expectKeyword("map") t := new(types.Map) @@ -665,7 +665,7 @@ func (p *parser) parseMapType(pkg *types.Package, nlist []interface{}) types.Typ } // ChanType = "chan" ["<-" | "-<"] Type . -func (p *parser) parseChanType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseChanType(pkg *types.Package, nlist []any) types.Type { p.expectKeyword("chan") t := new(types.Chan) @@ -692,7 +692,7 @@ func (p *parser) parseChanType(pkg *types.Package, nlist []interface{}) types.Ty } // StructType = "struct" "{" { Field } "}" . -func (p *parser) parseStructType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseStructType(pkg *types.Package, nlist []any) types.Type { p.expectKeyword("struct") t := new(types.Struct) @@ -759,14 +759,14 @@ func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { } // FunctionType = ParamList ResultList . -func (p *parser) parseFunctionType(pkg *types.Package, nlist []interface{}) *types.Signature { +func (p *parser) parseFunctionType(pkg *types.Package, nlist []any) *types.Signature { t := new(types.Signature) p.update(t, nlist) params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) - *t = *types.NewSignature(nil, params, results, isVariadic) + *t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic) return t } @@ -799,7 +799,7 @@ func (p *parser) parseFunc(pkg *types.Package) *types.Func { } // InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . -func (p *parser) parseInterfaceType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseInterfaceType(pkg *types.Package, nlist []any) types.Type { p.expectKeyword("interface") t := new(types.Interface) @@ -828,7 +828,7 @@ func (p *parser) parseInterfaceType(pkg *types.Package, nlist []interface{}) typ } // PointerType = "*" ("any" | Type) . -func (p *parser) parsePointerType(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parsePointerType(pkg *types.Package, nlist []any) types.Type { p.expect('*') if p.tok == scanner.Ident { p.expectKeyword("any") @@ -846,7 +846,7 @@ func (p *parser) parsePointerType(pkg *types.Package, nlist []interface{}) types } // TypeSpec = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . -func (p *parser) parseTypeSpec(pkg *types.Package, nlist []interface{}) types.Type { +func (p *parser) parseTypeSpec(pkg *types.Package, nlist []any) types.Type { switch p.tok { case scanner.String: return p.parseNamedType(nlist) @@ -935,14 +935,14 @@ func lookupBuiltinType(typ int) types.Type { // // parseType updates the type map to t for all type numbers n. // -func (p *parser) parseType(pkg *types.Package, n ...interface{}) types.Type { +func (p *parser) parseType(pkg *types.Package, n ...any) types.Type { p.expect('<') t, _ := p.parseTypeAfterAngle(pkg, n...) return t } // (*parser).Type after reading the "<". -func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) { +func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...any) (t types.Type, n1 int) { p.expectKeyword("type") n1 = 0 @@ -985,7 +985,7 @@ func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (t ty // parseTypeExtended is identical to parseType, but if the type in // question is a saved type, returns the index as well as the type // pointer (index returned is zero if we parsed a builtin). -func (p *parser) parseTypeExtended(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) { +func (p *parser) parseTypeExtended(pkg *types.Package, n ...any) (t types.Type, n1 int) { p.expect('<') t, n1 = p.parseTypeAfterAngle(pkg, n...) return @@ -1072,7 +1072,7 @@ func (p *parser) parseTypes(pkg *types.Package) { } // parseSavedType parses one saved type definition. -func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []interface{}) { +func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []any) { defer func(s *scanner.Scanner, tok rune, lit string) { p.scanner = s p.tok = tok diff --git a/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox b/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox Binary files differindex 1db8156..94ce039 100644 --- a/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox +++ b/libgo/go/go/internal/gccgoimporter/testdata/escapeinfo.gox diff --git a/libgo/go/go/internal/gccgoimporter/testdata/time.gox b/libgo/go/go/internal/gccgoimporter/testdata/time.gox Binary files differindex 80c2dbc..a6822ea 100644 --- a/libgo/go/go/internal/gccgoimporter/testdata/time.gox +++ b/libgo/go/go/internal/gccgoimporter/testdata/time.gox diff --git a/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox b/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox Binary files differindex e70e539..ae1a6f7 100644 --- a/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox +++ b/libgo/go/go/internal/gccgoimporter/testdata/unicode.gox diff --git a/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox b/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox Binary files differindex ea46841..d693fe6 100644 --- a/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox +++ b/libgo/go/go/internal/gccgoimporter/testdata/v1reflect.gox diff --git a/libgo/go/go/internal/gcimporter/gcimporter_test.go b/libgo/go/go/internal/gcimporter/gcimporter_test.go index 3c76aaf..c9c5946 100644 --- a/libgo/go/go/internal/gcimporter/gcimporter_test.go +++ b/libgo/go/go/internal/gcimporter/gcimporter_test.go @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gcimporter +package gcimporter_test import ( "bytes" "fmt" + "internal/goexperiment" "internal/testenv" "os" "os/exec" @@ -16,8 +17,13 @@ import ( "testing" "time" + "go/ast" + "go/importer" + "go/parser" "go/token" "go/types" + + . "go/internal/gcimporter" ) // skipSpecialPlatforms causes the test to be skipped for platforms where @@ -63,6 +69,8 @@ func testPath(t *testing.T, path, srcDir string) *types.Package { const maxTime = 30 * time.Second +var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go + func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) list, err := os.ReadDir(dirname) @@ -110,28 +118,153 @@ func TestImportTestdata(t *testing.T) { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) } + testfiles := map[string][]string{ + "exports.go": {"go/ast", "go/token"}, + } + if !goexperiment.Unified { + testfiles["generics.go"] = nil + } + + for testfile, wantImports := range testfiles { + tmpdir := mktmpdir(t) + defer os.RemoveAll(tmpdir) + + compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata")) + path := "./testdata/" + strings.TrimSuffix(testfile, ".go") + + if pkg := testPath(t, path, tmpdir); pkg != nil { + // The package's Imports list must include all packages + // explicitly imported by testfile, plus all packages + // referenced indirectly via exported objects in testfile. + got := fmt.Sprint(pkg.Imports()) + for _, want := range wantImports { + if !strings.Contains(got, want) { + t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) + } + } + } + } +} + +func TestImportTypeparamTests(t *testing.T) { + // This test doesn't yet work with the unified export format. + if goexperiment.Unified { + t.Skip("unified export data format is currently unsupported") + } + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + } + tmpdir := mktmpdir(t) defer os.RemoveAll(tmpdir) - compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata")) - - if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil { - // The package's Imports list must include all packages - // explicitly imported by exports.go, plus all packages - // referenced indirectly via exported objects in exports.go. - // With the textual export format, the list may also include - // additional packages that are not strictly required for - // import processing alone (they are exported to err "on - // the safe side"). - // TODO(gri) update the want list to be precise, now that - // the textual export data is gone. - got := fmt.Sprint(pkg.Imports()) - for _, want := range []string{"go/ast", "go/token"} { - if !strings.Contains(got, want) { - t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) + // Check go files in test/typeparam, except those that fail for a known + // reason. + rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") + list, err := os.ReadDir(rootDir) + if err != nil { + t.Fatal(err) + } + + skip := map[string]string{ + "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this. + "nested.go": "fails to compile", // TODO(rfindley): investigate this. + "issue50417.go": "inconsistent interface member sorting", + } + + for _, entry := range list { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { + // For now, only consider standalone go files. + continue + } + + t.Run(entry.Name(), func(t *testing.T) { + if reason, ok := skip[entry.Name()]; ok { + t.Skip(reason) } + + filename := filepath.Join(rootDir, entry.Name()) + src, err := os.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { + // We're bypassing the logic of run.go here, so be conservative about + // the files we consider in an attempt to make this test more robust to + // changes in test/typeparams. + t.Skipf("not detected as a run test") + } + + // Compile and import, and compare the resulting package with the package + // that was type-checked directly. + compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata")) + pkgName := strings.TrimSuffix(entry.Name(), ".go") + imported := importPkg(t, "./testdata/"+pkgName, tmpdir) + checked := checkFile(t, filename, src) + + seen := make(map[string]bool) + for _, name := range imported.Scope().Names() { + if !token.IsExported(name) { + continue // ignore synthetic names like .inittask and .dict.* + } + seen[name] = true + + importedObj := imported.Scope().Lookup(name) + got := types.ObjectString(importedObj, types.RelativeTo(imported)) + got = sanitizeObjectString(got) + + checkedObj := checked.Scope().Lookup(name) + if checkedObj == nil { + t.Fatalf("imported object %q was not type-checked", name) + } + want := types.ObjectString(checkedObj, types.RelativeTo(checked)) + want = sanitizeObjectString(want) + + if got != want { + t.Errorf("imported %q as %q, want %q", name, got, want) + } + } + + for _, name := range checked.Scope().Names() { + if !token.IsExported(name) || seen[name] { + continue + } + t.Errorf("did not import object %q", name) + } + }) + } +} + +// sanitizeObjectString removes type parameter debugging markers from an object +// string, to normalize it for comparison. +// TODO(rfindley): this should not be necessary. +func sanitizeObjectString(s string) string { + var runes []rune + for _, r := range s { + if '₀' <= r && r < '₀'+10 { + continue // trim type parameter subscripts } + runes = append(runes, r) + } + return string(runes) +} + +func checkFile(t *testing.T, filename string, src []byte) *types.Package { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, filename, src, 0) + if err != nil { + t.Fatal(err) } + config := types.Config{ + Importer: importer.Default(), + } + pkg, err := config.Check("", fset, []*ast.File{f}, nil) + if err != nil { + t.Fatal(err) + } + return pkg } func TestVersionHandling(t *testing.T) { @@ -258,7 +391,7 @@ var importedObjectTests = []struct { {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"}, // interfaces - {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"}, + {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, diff --git a/libgo/go/go/internal/gcimporter/iimport.go b/libgo/go/go/internal/gcimporter/iimport.go index 76d47d0..8ec4c54 100644 --- a/libgo/go/go/internal/gcimporter/iimport.go +++ b/libgo/go/go/internal/gcimporter/iimport.go @@ -18,6 +18,7 @@ import ( "io" "math/big" "sort" + "strings" ) type intReader struct { @@ -41,6 +42,21 @@ func (r *intReader) uint64() uint64 { return i } +// Keep this in sync with constants in iexport.go. +const ( + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + iexportVersionGenerics = 2 + iexportVersionGo1_18 = 2 + + iexportVersionCurrent = 2 +) + +type ident struct { + pkg string + name string +} + const predeclReserved = 32 type itag uint64 @@ -56,6 +72,9 @@ const ( signatureType structType interfaceType + typeParamType + instanceType + unionType ) // iImportData imports a package from the serialized package data @@ -63,7 +82,7 @@ const ( // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) { - const currentVersion = 1 + const currentVersion = iexportVersionCurrent version := int64(-1) defer func() { if e := recover(); e != nil { @@ -79,7 +98,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea version = int64(r.uint64()) switch version { - case currentVersion, 0: + case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: default: errorf("unknown iexport format version %d", version) } @@ -95,8 +114,9 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea declData := data[sLen:] p := iimporter{ - ipath: path, - version: int(version), + exportVersion: version, + ipath: path, + version: int(version), stringData: stringData, stringCache: make(map[uint64]string), @@ -105,12 +125,16 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea declData: declData, pkgIndex: make(map[*types.Package]map[string]uint64), typCache: make(map[uint64]types.Type), + // Separate map for typeparams, keyed by their package and unique + // name (name with subscript). + tparamIndex: make(map[ident]*types.TypeParam), fake: fakeFileSet{ fset: fset, - files: make(map[string]*token.File), + files: make(map[string]*fileInfo), }, } + defer p.fake.setLines() // set lines for files in fset for i, pt := range predeclared { p.typCache[uint64(i)] = pt @@ -172,16 +196,18 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea } type iimporter struct { - ipath string - version int + exportVersion int64 + ipath string + version int stringData []byte stringCache map[uint64]string pkgCache map[uint64]*types.Package - declData []byte - pkgIndex map[*types.Package]map[string]uint64 - typCache map[uint64]types.Type + declData []byte + pkgIndex map[*types.Package]map[string]uint64 + typCache map[uint64]types.Type + tparamIndex map[ident]*types.TypeParam fake fakeFileSet interfaceList []*types.Interface @@ -229,7 +255,7 @@ func (p *iimporter) pkgAt(off uint64) *types.Package { } func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { - if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) { + if t, ok := p.typCache[off]; ok && canReuse(base, t) { return t } @@ -241,12 +267,30 @@ func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { r.declReader.Reset(p.declData[off-predeclReserved:]) t := r.doType(base) - if base == nil || !isInterface(t) { + if canReuse(base, t) { p.typCache[off] = t } return t } +// canReuse reports whether the type rhs on the RHS of the declaration for def +// may be re-used. +// +// Specifically, if def is non-nil and rhs is an interface type with methods, it +// may not be re-used because we have a convention of setting the receiver type +// for interface methods to def. +func canReuse(def *types.Named, rhs types.Type) bool { + if def == nil { + return true + } + iface, _ := rhs.(*types.Interface) + if iface == nil { + return true + } + // Don't use iface.Empty() here as iface may not be complete. + return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0 +} + type importReader struct { p *iimporter declReader bytes.Reader @@ -271,17 +315,26 @@ func (r *importReader) obj(name string) { r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) - case 'F': - sig := r.signature(nil) - + case 'F', 'G': + var tparams []*types.TypeParam + if tag == 'G' { + tparams = r.tparamList() + } + sig := r.signature(nil, nil, tparams) r.declare(types.NewFunc(pos, r.currPkg, name, sig)) - case 'T': + case 'T', 'U': // Types can be recursive. We need to setup a stub // declaration before recursing. obj := types.NewTypeName(pos, r.currPkg, name, nil) named := types.NewNamed(obj, nil, nil) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + named.SetTypeParams(tparams) + } underlying := r.p.typAt(r.uint64(), named).Underlying() named.SetUnderlying(underlying) @@ -291,12 +344,55 @@ func (r *importReader) obj(name string) { mpos := r.pos() mname := r.ident() recv := r.param() - msig := r.signature(recv) + + // If the receiver has any targs, set those as the + // rparams of the method (since those are the + // typeparams being used in the method sig/body). + targs := baseType(recv.Type()).TypeArgs() + var rparams []*types.TypeParam + if targs.Len() > 0 { + rparams = make([]*types.TypeParam, targs.Len()) + for i := range rparams { + rparams[i], _ = targs.At(i).(*types.TypeParam) + } + } + msig := r.signature(recv, rparams, nil) named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) } } + case 'P': + // We need to "declare" a typeparam in order to have a name that + // can be referenced recursively (if needed) in the type param's + // bound. + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected type param type") + } + // Remove the "path" from the type param name that makes it unique, + // and revert any unique name used for blank typeparams. + name0 := tparamName(name) + tn := types.NewTypeName(pos, r.currPkg, name0, nil) + t := types.NewTypeParam(tn, nil) + // To handle recursive references to the typeparam within its + // bound, save the partial type in tparamIndex before reading the bounds. + id := ident{r.currPkg.Name(), name} + r.p.tparamIndex[id] = t + + var implicit bool + if r.p.exportVersion >= iexportVersionGo1_18 { + implicit = r.bool() + } + constraint := r.typ() + if implicit { + iface, _ := constraint.(*types.Interface) + if iface == nil { + errorf("non-interface constraint marked implicit") + } + iface.MarkImplicit() + } + t.SetConstraint(constraint) + case 'V': typ := r.typ() @@ -313,6 +409,10 @@ func (r *importReader) declare(obj types.Object) { func (r *importReader) value() (typ types.Type, val constant.Value) { typ = r.typ() + if r.p.exportVersion >= iexportVersionGo1_18 { + // TODO: add support for using the kind + _ = constant.Kind(r.int64()) + } switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { case types.IsBoolean: @@ -502,7 +602,7 @@ func (r *importReader) doType(base *types.Named) types.Type { return types.NewMap(r.typ(), r.typ()) case signatureType: r.currPkg = r.pkg() - return r.signature(nil) + return r.signature(nil, nil, nil) case structType: r.currPkg = r.pkg() @@ -542,13 +642,56 @@ func (r *importReader) doType(base *types.Named) types.Type { recv = types.NewVar(token.NoPos, r.currPkg, "", base) } - msig := r.signature(recv) + msig := r.signature(recv, nil, nil) methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) } typ := types.NewInterfaceType(methods, embeddeds) r.p.interfaceList = append(r.p.interfaceList, typ) return typ + + case typeParamType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected type param type") + } + pkg, name := r.qualifiedIdent() + id := ident{pkg.Name(), name} + if t, ok := r.p.tparamIndex[id]; ok { + // We're already in the process of importing this typeparam. + return t + } + // Otherwise, import the definition of the typeparam now. + r.p.doDecl(pkg, name) + return r.p.tparamIndex[id] + + case instanceType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + // pos does not matter for instances: they are positioned on the original + // type. + _ = r.pos() + len := r.uint64() + targs := make([]types.Type, len) + for i := range targs { + targs[i] = r.typ() + } + baseType := r.typ() + // The imported instantiated type doesn't include any methods, so + // we must always use the methods of the base (orig) type. + // TODO provide a non-nil *Context + t, _ := types.Instantiate(nil, baseType, targs, false) + return t + + case unionType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + terms := make([]*types.Term, r.uint64()) + for i := range terms { + terms[i] = types.NewTerm(r.bool(), r.typ()) + } + return types.NewUnion(terms) } } @@ -556,11 +699,23 @@ func (r *importReader) kind() itag { return itag(r.uint64()) } -func (r *importReader) signature(recv *types.Var) *types.Signature { +func (r *importReader) signature(recv *types.Var, rparams, tparams []*types.TypeParam) *types.Signature { params := r.paramList() results := r.paramList() variadic := params.Len() > 0 && r.bool() - return types.NewSignature(recv, params, results, variadic) + return types.NewSignatureType(recv, rparams, tparams, params, results, variadic) +} + +func (r *importReader) tparamList() []*types.TypeParam { + n := r.uint64() + if n == 0 { + return nil + } + xs := make([]*types.TypeParam, n) + for i := range xs { + xs[i], _ = r.typ().(*types.TypeParam) + } + return xs } func (r *importReader) paramList() *types.Tuple { @@ -605,3 +760,31 @@ func (r *importReader) byte() byte { } return x } + +func baseType(typ types.Type) *types.Named { + // pointer receivers are never types.Named types + if p, _ := typ.(*types.Pointer); p != nil { + typ = p.Elem() + } + // receiver base types are always (possibly generic) types.Named types + n, _ := typ.(*types.Named) + return n +} + +const blankMarker = "$" + +// tparamName returns the real name of a type parameter, after stripping its +// qualifying prefix and reverting blank-name encoding. See tparamExportName +// for details. +func tparamName(exportName string) string { + // Remove the "path" from the type param name that makes it unique. + ix := strings.LastIndex(exportName, ".") + if ix < 0 { + errorf("malformed type parameter export name %s: missing prefix", exportName) + } + name := exportName[ix+1:] + if strings.HasPrefix(name, blankMarker) { + return "_" + } + return name +} diff --git a/libgo/go/go/internal/gcimporter/support.go b/libgo/go/go/internal/gcimporter/support.go index b8bb14d..61d1b46 100644 --- a/libgo/go/go/internal/gcimporter/support.go +++ b/libgo/go/go/internal/gcimporter/support.go @@ -13,7 +13,7 @@ import ( "sync" ) -func errorf(format string, args ...interface{}) { +func errorf(format string, args ...any) { panic(fmt.Sprintf(format, args...)) } @@ -25,37 +25,50 @@ const deltaNewFile = -64 // Synthesize a token.Pos type fakeFileSet struct { fset *token.FileSet - files map[string]*token.File + files map[string]*fileInfo } +type fileInfo struct { + file *token.File + lastline int +} + +const maxlines = 64 * 1024 + func (s *fakeFileSet) pos(file string, line, column int) token.Pos { // TODO(mdempsky): Make use of column. - // Since we don't know the set of needed file positions, we - // reserve maxlines positions per file. - const maxlines = 64 * 1024 + // Since we don't know the set of needed file positions, we reserve + // maxlines positions per file. We delay calling token.File.SetLines until + // all positions have been calculated (by way of fakeFileSet.setLines), so + // that we can avoid setting unnecessary lines. See also golang/go#46586. f := s.files[file] if f == nil { - f = s.fset.AddFile(file, -1, maxlines) + f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)} s.files[file] = f - // Allocate the fake linebreak indices on first use. - // TODO(adonovan): opt: save ~512KB using a more complex scheme? - fakeLinesOnce.Do(func() { - fakeLines = make([]int, maxlines) - for i := range fakeLines { - fakeLines[i] = i - } - }) - f.SetLines(fakeLines) } if line > maxlines { line = 1 } + if line > f.lastline { + f.lastline = line + } - // Treat the file as if it contained only newlines - // and column=1: use the line number as the offset. - return f.Pos(line - 1) + // Return a fake position assuming that f.file consists only of newlines. + return token.Pos(f.file.Base() + line - 1) +} + +func (s *fakeFileSet) setLines() { + fakeLinesOnce.Do(func() { + fakeLines = make([]int, maxlines) + for i := range fakeLines { + fakeLines[i] = i + } + }) + for _, f := range s.files { + f.file.SetLines(fakeLines[:f.lastline]) + } } var ( @@ -121,7 +134,14 @@ var predeclared = []types.Type{ types.Typ[types.Invalid], // only appears in packages with errors // used internally by gc; never used by this package or in .a files + // not to be confused with the universe any anyType{}, + + // comparable + types.Universe.Lookup("comparable").Type(), + + // any + types.Universe.Lookup("any").Type(), } type anyType struct{} diff --git a/libgo/go/go/internal/gcimporter/testdata/exports.go b/libgo/go/go/internal/gcimporter/testdata/exports.go index 8ba3242..3d5a8c9 100644 --- a/libgo/go/go/internal/gcimporter/testdata/exports.go +++ b/libgo/go/go/internal/gcimporter/testdata/exports.go @@ -15,14 +15,17 @@ const init1 = 0 func init() {} const ( - C0 int = 0 - C1 = 3.14159265 - C2 = 2.718281828i - C3 = -123.456e-789 - C4 = +123.456e+789 - C5 = 1234i - C6 = "foo\n" - C7 = `bar\n` + C0 int = 0 + C1 = 3.14159265 + C2 = 2.718281828i + C3 = -123.456e-789 + C4 = +123.456e+789 + C5 = 1234i + C6 = "foo\n" + C7 = `bar\n` + C8 = 42 + C9 int = 42 + C10 float64 = 42 ) type ( @@ -47,7 +50,7 @@ type ( _ *T10 } T11 map[int]string - T12 interface{} + T12 any T13 interface { m1() m2(int) float32 @@ -62,7 +65,7 @@ type ( T17 func(x int) T18 func() float32 T19 func() (x float32) - T20 func(...interface{}) + T20 func(...any) T21 struct{ next *T21 } T22 struct{ link *T23 } T23 struct{ link *T22 } @@ -83,6 +86,6 @@ func F1() {} func F2(x int) {} func F3() int { return 0 } func F4() float32 { return 0 } -func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10) +func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...any) (p, q, r chan<- T10) func (p *T1) M1() diff --git a/libgo/go/go/internal/gcimporter/testdata/generics.go b/libgo/go/go/internal/gcimporter/testdata/generics.go new file mode 100644 index 0000000..00bf040 --- /dev/null +++ b/libgo/go/go/internal/gcimporter/testdata/generics.go @@ -0,0 +1,29 @@ +// 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. + +// This file is used to generate an object file which +// serves as test file for gcimporter_test.go. + +package generics + +type Any any + +var x any + +type T[A, B any] struct { + Left A + Right B +} + +var X T[int, string] = T[int, string]{1, "hi"} + +func ToInt[P interface{ ~int }](p P) int { return int(p) } + +var IntID = ToInt[int] + +type G[C comparable] int + +func ImplicitFunc[T ~int]() {} + +type ImplicitType[T ~int] int diff --git a/libgo/go/go/internal/typeparams/common.go b/libgo/go/go/internal/typeparams/common.go index 47b8f7c..9b82e60 100644 --- a/libgo/go/go/internal/typeparams/common.go +++ b/libgo/go/go/internal/typeparams/common.go @@ -7,7 +7,9 @@ // constraint. package typeparams -// DisallowParsing is the numeric value of a parsing mode that disallows type -// parameters. This only matters if the typeparams experiment is active, and -// may be used for running tests that disallow generics. -const DisallowParsing = 1 << 30 +// 'Hidden' parser modes to control the parsing of type-parameter related +// features. +const ( + DisallowTypeSets = 1 << 29 // Disallow eliding 'interface' in constraint type sets. + DisallowParsing = 1 << 30 // Disallow type parameters entirely. +) diff --git a/libgo/go/go/internal/typeparams/notypeparams.go b/libgo/go/go/internal/typeparams/notypeparams.go deleted file mode 100644 index 2ceafaa..0000000 --- a/libgo/go/go/internal/typeparams/notypeparams.go +++ /dev/null @@ -1,40 +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. - -//go:build !typeparams -// +build !typeparams - -package typeparams - -import ( - "go/ast" -) - -const Enabled = false - -func PackExpr(list []ast.Expr) ast.Expr { - switch len(list) { - case 1: - return list[0] - default: - // The parser should not attempt to pack multiple expressions into an - // IndexExpr if type params are disabled. - panic("multiple index expressions are unsupported without type params") - } -} - -func UnpackExpr(expr ast.Expr) []ast.Expr { - return []ast.Expr{expr} -} - -func IsListExpr(n ast.Node) bool { - return false -} - -func Get(ast.Node) *ast.FieldList { - return nil -} - -func Set(node ast.Node, params *ast.FieldList) { -} diff --git a/libgo/go/go/internal/typeparams/typeparams.go b/libgo/go/go/internal/typeparams/typeparams.go index 871e95d..3f84f2f 100644 --- a/libgo/go/go/internal/typeparams/typeparams.go +++ b/libgo/go/go/internal/typeparams/typeparams.go @@ -2,68 +2,53 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build typeparams -// +build typeparams - package typeparams import ( - "fmt" "go/ast" + "go/token" ) -const Enabled = true - -func PackExpr(list []ast.Expr) ast.Expr { - switch len(list) { +func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr { + switch len(exprs) { case 0: - // Return an empty ListExpr here, rather than nil, as IndexExpr.Index must - // never be nil. - // TODO(rFindley) would a BadExpr be more appropriate here? - return &ast.ListExpr{} + panic("internal error: PackIndexExpr with empty expr slice") case 1: - return list[0] + return &ast.IndexExpr{ + X: x, + Lbrack: lbrack, + Index: exprs[0], + Rbrack: rbrack, + } default: - return &ast.ListExpr{ElemList: list} + return &ast.IndexListExpr{ + X: x, + Lbrack: lbrack, + Indices: exprs, + Rbrack: rbrack, + } } } -// TODO(gri) Should find a more efficient solution that doesn't -// require introduction of a new slice for simple -// expressions. -func UnpackExpr(x ast.Expr) []ast.Expr { - if x, _ := x.(*ast.ListExpr); x != nil { - return x.ElemList - } - if x != nil { - return []ast.Expr{x} - } - return nil -} - -func IsListExpr(n ast.Node) bool { - _, ok := n.(*ast.ListExpr) - return ok +// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr. +// +// Orig holds the original ast.Expr from which this IndexExpr was derived. +type IndexExpr struct { + Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below. + *ast.IndexListExpr } -func Get(n ast.Node) *ast.FieldList { - switch n := n.(type) { - case *ast.TypeSpec: - return n.TParams - case *ast.FuncType: - return n.TParams - default: - panic(fmt.Sprintf("node type %T has no type parameters", n)) - } -} - -func Set(n ast.Node, params *ast.FieldList) { - switch n := n.(type) { - case *ast.TypeSpec: - n.TParams = params - case *ast.FuncType: - n.TParams = params - default: - panic(fmt.Sprintf("node type %T has no type parameters", n)) +func UnpackIndexExpr(n ast.Node) *IndexExpr { + switch e := n.(type) { + case *ast.IndexExpr: + return &IndexExpr{e, &ast.IndexListExpr{ + X: e.X, + Lbrack: e.Lbrack, + Indices: []ast.Expr{e.Index}, + Rbrack: e.Rbrack, + }} + case *ast.IndexListExpr: + return &IndexExpr{e, e} } + return nil } diff --git a/libgo/go/go/parser/error_test.go b/libgo/go/go/parser/error_test.go index f4f0a52..bedfc26 100644 --- a/libgo/go/go/parser/error_test.go +++ b/libgo/go/go/parser/error_test.go @@ -23,6 +23,7 @@ package parser import ( + "flag" "go/internal/typeparams" "go/scanner" "go/token" @@ -33,6 +34,8 @@ import ( "testing" ) +var traceErrs = flag.Bool("trace_errs", false, "whether to enable tracing for error tests") + const testdata = "testdata" // getFile assumes that each filename occurs at most once @@ -151,7 +154,7 @@ func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]str } } -func checkErrors(t *testing.T, filename string, input interface{}, mode Mode, expectErrors bool) { +func checkErrors(t *testing.T, filename string, input any, mode Mode, expectErrors bool) { t.Helper() src, err := readSource(filename, input) if err != nil { @@ -186,16 +189,17 @@ func TestErrors(t *testing.T) { } for _, d := range list { name := d.Name() - if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) { - mode := DeclarationErrors | AllErrors - if strings.HasSuffix(name, ".go2") { - if !typeparams.Enabled { - continue + t.Run(name, func(t *testing.T) { + if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) { + mode := DeclarationErrors | AllErrors + if !strings.HasSuffix(name, ".go2") { + mode |= typeparams.DisallowParsing } - } else { - mode |= typeparams.DisallowParsing + if *traceErrs { + mode |= Trace + } + checkErrors(t, filepath.Join(testdata, name), nil, mode, true) } - checkErrors(t, filepath.Join(testdata, name), nil, mode, true) - } + }) } } diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go index 85486d2..e4f8c28 100644 --- a/libgo/go/go/parser/interface.go +++ b/libgo/go/go/parser/interface.go @@ -22,7 +22,7 @@ import ( // otherwise it returns an error. If src == nil, readSource returns // the result of reading the file specified by filename. // -func readSource(filename string, src interface{}) ([]byte, error) { +func readSource(filename string, src any) ([]byte, error) { if src != nil { switch s := src.(type) { case string: @@ -82,7 +82,7 @@ const ( // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by source position. // -func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) { +func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) { if fset == nil { panic("parser.ParseFile: no token.FileSet provided (fset == nil)") } @@ -188,7 +188,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, m // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by source position. // -func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (expr ast.Expr, err error) { +func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (expr ast.Expr, err error) { if fset == nil { panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)") } diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index f10c865..e456e29 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -76,14 +76,13 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod p.next() } -func (p *parser) parseTypeParams() bool { - return typeparams.Enabled && p.mode&typeparams.DisallowParsing == 0 -} +func (p *parser) allowGenerics() bool { return p.mode&typeparams.DisallowParsing == 0 } +func (p *parser) allowTypeSets() bool { return p.mode&typeparams.DisallowTypeSets == 0 } // ---------------------------------------------------------------------------- // Parsing support -func (p *parser) printTrace(a ...interface{}) { +func (p *parser) printTrace(a ...any) { const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " const n = len(dots) pos := p.file.Position(p.pos) @@ -499,7 +498,7 @@ func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr { } typ := p.parseTypeName(ident) - if p.tok == token.LBRACK && p.parseTypeParams() { + if p.tok == token.LBRACK && p.allowGenerics() { typ = p.parseTypeInstance(typ) } @@ -526,23 +525,27 @@ func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr { return ident } -func (p *parser) parseArrayLen() ast.Expr { +// "[" has already been consumed, and lbrack is its position. +// If len != nil it is the already consumed array length. +func (p *parser) parseArrayType(lbrack token.Pos, len ast.Expr) *ast.ArrayType { if p.trace { - defer un(trace(p, "ArrayLen")) + defer un(trace(p, "ArrayType")) } - p.exprLev++ - var len ast.Expr - // always permit ellipsis for more fault-tolerant parsing - if p.tok == token.ELLIPSIS { - len = &ast.Ellipsis{Ellipsis: p.pos} - p.next() - } else if p.tok != token.RBRACK { - len = p.parseRhs() + if len == nil { + p.exprLev++ + // always permit ellipsis for more fault-tolerant parsing + if p.tok == token.ELLIPSIS { + len = &ast.Ellipsis{Ellipsis: p.pos} + p.next() + } else if p.tok != token.RBRACK { + len = p.parseRhs() + } + p.exprLev-- } - p.exprLev-- - - return len + p.expect(token.RBRACK) + elt := p.parseType() + return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt} } func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Expr) { @@ -558,7 +561,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex // TODO(rfindley): consider changing parseRhsOrType so that this function variable // is not needed. argparser := p.parseRhsOrType - if !p.parseTypeParams() { + if !p.allowGenerics() { argparser = p.parseRhs } if p.tok != token.RBRACK { @@ -588,19 +591,19 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex // x [P]E return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt} } - if !p.parseTypeParams() { + if !p.allowGenerics() { p.error(rbrack, "missing element type in array type expression") return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()} } } - if !p.parseTypeParams() { + if !p.allowGenerics() { p.error(firstComma, "expected ']', found ','") return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()} } // x[P], x[P1, P2], ... - return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack} + return nil, typeparams.PackIndexExpr(x, lbrack, args, rbrack) } func (p *parser) parseFieldDecl() *ast.Field { @@ -711,8 +714,9 @@ type field struct { typ ast.Expr } -func (p *parser) parseParamDecl(name *ast.Ident) (f field) { - // TODO(rFindley) compare with parser.paramDeclOrNil in the syntax package +func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) { + // TODO(rFindley) refactor to be more similar to paramDeclOrNil in the syntax + // package if p.trace { defer un(trace(p, "ParamDeclOrNil")) } @@ -720,10 +724,14 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) { ptok := p.tok if name != nil { p.tok = token.IDENT // force token.IDENT case in switch below + } else if typeSetsOK && p.tok == token.TILDE { + // "~" ... + return field{nil, p.embeddedElem(nil)} } switch p.tok { case token.IDENT: + // name if name != nil { f.name = name p.tok = ptok @@ -736,17 +744,32 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) { f.typ = p.parseType() case token.LBRACK: - // name[type1, type2, ...] or name []type or name [len]type + // name "[" type1, ..., typeN "]" or name "[" n "]" type f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name) case token.ELLIPSIS: - // name ...type + // name "..." type f.typ = p.parseDotsType() + return // don't allow ...type "|" ... case token.PERIOD: - // qualified.typename + // name "." ... f.typ = p.parseQualifiedIdent(f.name) f.name = nil + + case token.TILDE: + if typeSetsOK { + f.typ = p.embeddedElem(nil) + return + } + + case token.OR: + if typeSetsOK { + // name "|" typeset + f.typ = p.embeddedElem(f.name) + f.name = nil + return + } } case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN: @@ -754,23 +777,36 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) { f.typ = p.parseType() case token.ELLIPSIS: - // ...type + // "..." type // (always accepted) f.typ = p.parseDotsType() + return // don't allow ...type "|" ... default: + // TODO(rfindley): this looks incorrect in the case of type parameter + // lists. p.errorExpected(p.pos, ")") p.advance(exprEnd) } + // [name] type "|" + if typeSetsOK && p.tok == token.OR && f.typ != nil { + f.typ = p.embeddedElem(f.typ) + } + return } -func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) { +func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token) (params []*ast.Field) { if p.trace { defer un(trace(p, "ParameterList")) } + // Type parameters are the only parameter list closed by ']'. + tparams := closing == token.RBRACK + // Type set notation is ok in type parameter lists. + typeSetsOK := tparams && p.allowTypeSets() + pos := p.pos if name0 != nil { pos = name0.Pos() @@ -780,7 +816,7 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse var named int // number of parameters that have an explicit name and type for name0 != nil || p.tok != closing && p.tok != token.EOF { - par := parseParamDecl(name0) + par := p.parseParamDecl(name0, typeSetsOK) name0 = nil // 1st name was consumed if present if par.name != nil || par.typ != nil { list = append(list, par) @@ -818,11 +854,13 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse // some named => all must be named ok := true var typ ast.Expr + missingName := pos for i := len(list) - 1; i >= 0; i-- { if par := &list[i]; par.typ != nil { typ = par.typ if par.name == nil { ok = false + missingName = par.typ.Pos() n := ast.NewIdent("_") n.NamePos = typ.Pos() // correct position par.name = n @@ -832,12 +870,13 @@ func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parse } else { // par.typ == nil && typ == nil => we only have a par.name ok = false + missingName = par.name.Pos() par.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos} } } if !ok { if tparams { - p.error(pos, "all type parameters must be named") + p.error(missingName, "all type parameters must be named") } else { p.error(pos, "mixed named and unnamed parameters") } @@ -883,11 +922,11 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field defer un(trace(p, "Parameters")) } - if p.parseTypeParams() && acceptTParams && p.tok == token.LBRACK { + if p.allowGenerics() && acceptTParams && p.tok == token.LBRACK { opening := p.pos p.next() // [T any](params) syntax - list := p.parseParameterList(nil, token.RBRACK, p.parseParamDecl, true) + list := p.parseParameterList(nil, token.RBRACK) rbrack := p.expect(token.RBRACK) tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack} // Type parameter lists must not be empty. @@ -901,7 +940,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field var fields []*ast.Field if p.tok != token.RPAREN { - fields = p.parseParameterList(nil, token.RPAREN, p.parseParamDecl, false) + fields = p.parseParameterList(nil, token.RPAREN) } rparen := p.expect(token.RPAREN) @@ -956,7 +995,7 @@ func (p *parser) parseMethodSpec() *ast.Field { x := p.parseTypeName(nil) if ident, _ := x.(*ast.Ident); ident != nil { switch { - case p.tok == token.LBRACK && p.parseTypeParams(): + case p.tok == token.LBRACK && p.allowGenerics(): // generic method or embedded instantiated type lbrack := p.pos p.next() @@ -965,21 +1004,26 @@ 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, p.parseParamDecl, true) + list := p.parseParameterList(name0, token.RBRACK) rbrack := p.expect(token.RBRACK) tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack} // 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, Params: params, Results: results} - typeparams.Set(typ, tparams) + typ = &ast.FuncType{ + Func: token.NoPos, + TypeParams: tparams, + Params: params, + Results: results, + } } else { // embedded instantiated type // TODO(rfindley) should resolve all identifiers in x. list := []ast.Expr{x} if p.atComma("type argument list", token.RBRACK) { p.exprLev++ + p.next() for p.tok != token.RBRACK && p.tok != token.EOF { list = append(list, p.parseType()) if !p.atComma("type argument list", token.RBRACK) { @@ -990,7 +1034,7 @@ func (p *parser) parseMethodSpec() *ast.Field { p.exprLev-- } rbrack := p.expectClosing(token.RBRACK, "type argument list") - typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: typeparams.PackExpr(list), Rbrack: rbrack} + typ = typeparams.PackIndexExpr(ident, lbrack, list, rbrack) } case p.tok == token.LPAREN: // ordinary method @@ -1006,16 +1050,60 @@ func (p *parser) parseMethodSpec() *ast.Field { } else { // embedded, possibly instantiated type typ = x - if p.tok == token.LBRACK && p.parseTypeParams() { + if p.tok == token.LBRACK && p.allowGenerics() { // embedded instantiated interface typ = p.parseTypeInstance(typ) } } - p.expectSemi() // call before accessing p.linecomment - spec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment} + // Comment is added at the callsite: the field below may joined with + // additional type specs using '|'. + // TODO(rfindley) this should be refactored. + // TODO(rfindley) add more tests for comment handling. + return &ast.Field{Doc: doc, Names: idents, Type: typ} +} - return spec +func (p *parser) embeddedElem(x ast.Expr) ast.Expr { + if p.trace { + defer un(trace(p, "EmbeddedElem")) + } + if x == nil { + x = p.embeddedTerm() + } + for p.tok == token.OR { + t := new(ast.BinaryExpr) + t.OpPos = p.pos + t.Op = token.OR + p.next() + t.X = x + t.Y = p.embeddedTerm() + x = t + } + return x +} + +func (p *parser) embeddedTerm() ast.Expr { + if p.trace { + defer un(trace(p, "EmbeddedTerm")) + } + if p.tok == token.TILDE { + t := new(ast.UnaryExpr) + t.OpPos = p.pos + t.Op = token.TILDE + p.next() + t.X = p.parseType() + return t + } + + t := p.tryIdentOrType() + if t == nil { + pos := p.pos + p.errorExpected(pos, "~ term or type") + p.advance(exprEnd) + return &ast.BadExpr{From: pos, To: p.pos} + } + + return t } func (p *parser) parseInterfaceType() *ast.InterfaceType { @@ -1025,22 +1113,39 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { pos := p.expect(token.INTERFACE) lbrace := p.expect(token.LBRACE) + var list []*ast.Field - for p.tok == token.IDENT || p.parseTypeParams() && p.tok == token.TYPE { - if p.tok == token.IDENT { - list = append(list, p.parseMethodSpec()) - } else { - // all types in a type list share the same field name "type" - // (since type is a keyword, a Go program cannot have that field name) - name := []*ast.Ident{{NamePos: p.pos, Name: "type"}} - p.next() - // add each type as a field named "type" - for _, typ := range p.parseTypeList() { - list = append(list, &ast.Field{Names: name, Type: typ}) + +parseElements: + for { + switch { + case p.tok == token.IDENT: + f := p.parseMethodSpec() + if f.Names == nil && p.allowGenerics() { + f.Type = p.embeddedElem(f.Type) } p.expectSemi() + f.Comment = p.lineComment + list = append(list, f) + case p.tok == token.TILDE && p.allowGenerics(): + typ := p.embeddedElem(nil) + p.expectSemi() + comment := p.lineComment + list = append(list, &ast.Field{Type: typ, Comment: comment}) + case p.allowGenerics(): + if t := p.tryIdentOrType(); t != nil { + typ := p.embeddedElem(t) + p.expectSemi() + comment := p.lineComment + list = append(list, &ast.Field{Type: typ, Comment: comment}) + } else { + break parseElements + } + default: + break parseElements } } + // TODO(rfindley): the error produced here could be improved, since we could // accept a identifier, 'type', or a '}' at this point. rbrace := p.expect(token.RBRACE) @@ -1095,13 +1200,12 @@ func (p *parser) parseChanType() *ast.ChanType { } func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr { - assert(p.parseTypeParams(), "parseTypeInstance while not parsing type params") + assert(p.allowGenerics(), "parseTypeInstance while not parsing type params") if p.trace { defer un(trace(p, "TypeInstance")) } opening := p.expect(token.LBRACK) - p.exprLev++ var list []ast.Expr for p.tok != token.RBRACK && p.tok != token.EOF { @@ -1115,23 +1219,30 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr { closing := p.expectClosing(token.RBRACK, "type argument list") - return &ast.IndexExpr{X: typ, Lbrack: opening, Index: typeparams.PackExpr(list), Rbrack: closing} + if len(list) == 0 { + p.errorExpected(closing, "type argument list") + return &ast.IndexExpr{ + X: typ, + Lbrack: opening, + Index: &ast.BadExpr{From: opening + 1, To: closing}, + Rbrack: closing, + } + } + + return typeparams.PackIndexExpr(typ, opening, list, closing) } func (p *parser) tryIdentOrType() ast.Expr { switch p.tok { case token.IDENT: typ := p.parseTypeName(nil) - if p.tok == token.LBRACK && p.parseTypeParams() { + if p.tok == token.LBRACK && p.allowGenerics() { typ = p.parseTypeInstance(typ) } return typ case token.LBRACK: lbrack := p.expect(token.LBRACK) - alen := p.parseArrayLen() - p.expect(token.RBRACK) - elt := p.parseType() - return &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt} + return p.parseArrayType(lbrack, nil) case token.STRUCT: return p.parseStructType() case token.MUL: @@ -1372,13 +1483,13 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr { return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack} } - if !p.parseTypeParams() { + if !p.allowGenerics() { p.error(firstComma, "expected ']' or ':', found ','") return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()} } // instance expression - return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack} + return typeparams.PackIndexExpr(x, lbrack, args, rbrack) } func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { @@ -1480,6 +1591,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { panic("unreachable") case *ast.SelectorExpr: case *ast.IndexExpr: + case *ast.IndexListExpr: case *ast.SliceExpr: case *ast.TypeAssertExpr: // If t.Type == nil we have a type assertion of the form @@ -1525,12 +1637,14 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { return x } -func (p *parser) parsePrimaryExpr() (x ast.Expr) { +func (p *parser) parsePrimaryExpr(x ast.Expr) ast.Expr { if p.trace { defer un(trace(p, "PrimaryExpr")) } - x = p.parseOperand() + if x == nil { + x = p.parseOperand() + } for { switch p.tok { case token.PERIOD: @@ -1566,18 +1680,18 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) { switch t.(type) { case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr: if p.exprLev < 0 { - return + return x } // x is possibly a composite literal type - case *ast.IndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: if p.exprLev < 0 { - return + return x } // x is possibly a composite literal type case *ast.ArrayType, *ast.StructType, *ast.MapType: // x is a composite literal type default: - return + return x } if t != x { p.error(t.Pos(), "cannot parenthesize type in composite literal") @@ -1585,7 +1699,7 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) { } x = p.parseLiteralValue(x) default: - return + return x } } } @@ -1656,7 +1770,7 @@ func (p *parser) parseUnaryExpr() ast.Expr { return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)} } - return p.parsePrimaryExpr() + return p.parsePrimaryExpr(nil) } func (p *parser) tokPrec() (token.Token, int) { @@ -1667,19 +1781,21 @@ func (p *parser) tokPrec() (token.Token, int) { return tok, tok.Precedence() } -func (p *parser) parseBinaryExpr(prec1 int) ast.Expr { +func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int) ast.Expr { if p.trace { defer un(trace(p, "BinaryExpr")) } - x := p.parseUnaryExpr() + if x == nil { + x = p.parseUnaryExpr() + } for { op, oprec := p.tokPrec() if oprec < prec1 { return x } pos := p.expect(op) - y := p.parseBinaryExpr(oprec + 1) + y := p.parseBinaryExpr(nil, oprec+1) x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)} } } @@ -1692,7 +1808,7 @@ func (p *parser) parseExpr() ast.Expr { defer un(trace(p, "Expression")) } - return p.parseBinaryExpr(token.LowestPrec + 1) + return p.parseBinaryExpr(nil, token.LowestPrec+1) } func (p *parser) parseRhs() ast.Expr { @@ -2415,13 +2531,19 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke return spec } -func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) { - list := p.parseParameterList(name0, closeTok, p.parseParamDecl, true) - closePos := p.expect(closeTok) - typeparams.Set(spec, &ast.FieldList{Opening: openPos, List: list, Closing: closePos}) - // Type alias cannot have type parameters. Accept them for robustness but complain. +func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident) { + if p.trace { + defer un(trace(p, "parseGenericType")) + } + + list := p.parseParameterList(name0, token.RBRACK) + closePos := p.expect(token.RBRACK) + spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos} + // Let the type checker decide whether to accept type parameters on aliases: + // see issue #46477. if p.tok == token.ASSIGN { - p.error(p.pos, "generic type cannot be alias") + // type alias + spec.Assign = p.pos p.next() } spec.Type = p.parseType() @@ -2435,34 +2557,42 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token ident := p.parseIdent() spec := &ast.TypeSpec{Doc: doc, Name: ident} - switch p.tok { - case token.LBRACK: + if p.tok == token.LBRACK && p.allowGenerics() { lbrack := p.pos p.next() if p.tok == token.IDENT { - // array type or generic type [T any] - p.exprLev++ - x := p.parseExpr() - p.exprLev-- - if name0, _ := x.(*ast.Ident); p.parseTypeParams() && name0 != nil && p.tok != token.RBRACK { + // array type or generic type: [name0... + name0 := p.parseIdent() + + // Index or slice expressions are never constant and thus invalid + // array length expressions. Thus, if we see a "[" following name + // we can safely assume that "[" name starts a type parameter list. + var x ast.Expr // x != nil means x is the array length expression + if p.tok != token.LBRACK { + // We may still have either an array type or generic type -- check if + // name0 is the entire expr. + p.exprLev++ + lhs := p.parsePrimaryExpr(name0) + x = p.parseBinaryExpr(lhs, token.LowestPrec+1) + p.exprLev-- + if x == name0 && p.tok != token.RBRACK { + x = nil + } + } + + if x == nil { // generic type [T any]; - p.parseGenericType(spec, lbrack, name0, token.RBRACK) + p.parseGenericType(spec, lbrack, name0) } else { // array type // TODO(rfindley) should resolve all identifiers in x. - p.expect(token.RBRACK) - elt := p.parseType() - spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt} + spec.Type = p.parseArrayType(lbrack, x) } } else { // array type - alen := p.parseArrayLen() - p.expect(token.RBRACK) - elt := p.parseType() - spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt} + spec.Type = p.parseArrayType(lbrack, nil) } - - default: + } else { // no type parameters if p.tok == token.ASSIGN { // type alias @@ -2528,10 +2658,11 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { results := p.parseResult() var body *ast.BlockStmt - if p.tok == token.LBRACE { + switch p.tok { + case token.LBRACE: body = p.parseBody() p.expectSemi() - } else if p.tok == token.SEMICOLON { + case token.SEMICOLON: p.next() if p.tok == token.LBRACE { // opening { of function declaration on next line @@ -2539,7 +2670,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { body = p.parseBody() p.expectSemi() } - } else { + default: p.expectSemi() } @@ -2548,13 +2679,13 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { Recv: recv, Name: ident, Type: &ast.FuncType{ - Func: pos, - Params: params, - Results: results, + Func: pos, + TypeParams: tparams, + Params: params, + Results: results, }, Body: body, } - typeparams.Set(decl.Type, tparams) return decl } diff --git a/libgo/go/go/parser/resolver.go b/libgo/go/go/parser/resolver.go index cf92c7e..910ca06 100644 --- a/libgo/go/go/parser/resolver.go +++ b/libgo/go/go/parser/resolver.go @@ -7,7 +7,6 @@ package parser import ( "fmt" "go/ast" - "go/internal/typeparams" "go/token" ) @@ -68,11 +67,11 @@ type resolver struct { targetStack [][]*ast.Ident // stack of unresolved labels } -func (r *resolver) dump(format string, args ...interface{}) { +func (r *resolver) dump(format string, args ...any) { fmt.Println(">>> " + r.sprintf(format, args...)) } -func (r *resolver) sprintf(format string, args ...interface{}) string { +func (r *resolver) sprintf(format string, args ...any) string { for i, arg := range args { switch arg := arg.(type) { case token.Pos: @@ -116,14 +115,8 @@ func (r *resolver) closeLabelScope() { r.labelScope = r.labelScope.Outer } -func (r *resolver) declare(decl, data interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { +func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { for _, ident := range idents { - // "type" is used for type lists in interfaces, and is otherwise an invalid - // identifier. The 'type' identifier is also artificially duplicated in the - // type list, so could cause panics below if we were to proceed. - if ident.Name == "type" { - continue - } assert(ident.Obj == nil, "identifier already declared or resolved") obj := ast.NewObj(kind, ident.Name) // remember the corresponding declaration for redeclaration @@ -189,10 +182,9 @@ 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)) } - // '_' and 'type' should never refer to existing declarations: '_' because it - // has special handling in the spec, and 'type' because it is a keyword, and - // only valid in an interface type list. - if ident.Name == "_" || ident.Name == "type" { + // '_' should never refer to existing declarations, because it has special + // handling in the spec. + if ident.Name == "_" { return } for s := r.topScope; s != nil; s = s.Outer { @@ -455,10 +447,10 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor { // at the identifier in the TypeSpec and ends at the end of the innermost // containing block. r.declare(spec, nil, r.topScope, ast.Typ, spec.Name) - if tparams := typeparams.Get(spec); tparams != nil { + if spec.TypeParams != nil { r.openScope(spec.Pos()) defer r.closeScope() - r.walkTParams(tparams) + r.walkTParams(spec.TypeParams) } ast.Walk(r, spec.Type) } @@ -474,8 +466,8 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor { // Type parameters are walked normally: they can reference each other, and // can be referenced by normal parameters. - if tparams := typeparams.Get(n.Type); tparams != nil { - r.walkTParams(tparams) + if n.Type.TypeParams != nil { + r.walkTParams(n.Type.TypeParams) // TODO(rFindley): need to address receiver type parameters. } @@ -500,7 +492,7 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor { } func (r *resolver) walkFuncType(typ *ast.FuncType) { - // typ.TParams must be walked separately for FuncDecls. + // typ.TypeParams must be walked separately for FuncDecls. r.resolveList(typ.Params) r.resolveList(typ.Results) r.declareList(typ.Params, ast.Var) @@ -539,9 +531,6 @@ func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) { // that they may be resolved in the constraint expressions held in the field // Type. func (r *resolver) walkTParams(list *ast.FieldList) { - if list == nil { - return - } r.declareList(list, ast.Typ) r.resolveList(list) } diff --git a/libgo/go/go/parser/resolver_test.go b/libgo/go/go/parser/resolver_test.go index 625c009..0c06c59 100644 --- a/libgo/go/go/parser/resolver_test.go +++ b/libgo/go/go/parser/resolver_test.go @@ -41,11 +41,7 @@ func TestResolution(t *testing.T) { path := filepath.Join(dir, fi.Name()) src := readFile(path) // panics on failure var mode Mode - if strings.HasSuffix(path, ".go2") { - if !typeparams.Enabled { - t.Skip("type params are not enabled") - } - } else { + if !strings.HasSuffix(path, ".go2") { mode |= typeparams.DisallowParsing } file, err := ParseFile(fset, path, src, mode) diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go index 67fef15..90a4ec9 100644 --- a/libgo/go/go/parser/short_test.go +++ b/libgo/go/go/parser/short_test.go @@ -119,11 +119,11 @@ var validWithTParamsOnly = []string{ `package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`, `package p; type _ struct{ T[P] /* ERROR "missing element type" */ }`, `package p; type _ struct{ T[struct /* ERROR "expected expression" */ {a, b, c int}] }`, - `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int}`, - `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int, float32; type bool; m(); type 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 T[P any /* ERROR "expected ']'" */ ] = T0`, } func TestValid(t *testing.T) { @@ -133,9 +133,6 @@ func TestValid(t *testing.T) { } }) t.Run("tparams", func(t *testing.T) { - if !typeparams.Enabled { - t.Skip("type params are not enabled") - } for _, src := range valids { checkErrors(t, src, src, DeclarationErrors|AllErrors, false) } @@ -200,10 +197,12 @@ var invalids = []string{ `package p; func (type /* ERROR "found 'type'" */ T)(T) _()`, `package p; type _[A+B, /* ERROR "expected ']'" */ ] int`, - // TODO: this error should be positioned on the ':' + // TODO(rfindley): this error should be positioned on the ':' `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`, - // TODO: the compiler error is better here: "cannot parenthesize embedded type" - `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`, + + // TODO(rfindley): the compiler error is better here: "cannot parenthesize embedded type" + // TODO(rfindley): confirm that parenthesized types should now be accepted. + // `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`, // issue 8656 `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, @@ -242,7 +241,6 @@ var invalidNoTParamErrs = []string{ // error messages produced when ParseTypeParams is set. var invalidTParamErrs = []string{ `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`, - `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`, `package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`, `package p; func _[]/* ERROR "empty type parameter list" */()`, @@ -266,9 +264,6 @@ func TestInvalid(t *testing.T) { } }) t.Run("tparams", func(t *testing.T) { - if !typeparams.Enabled { - t.Skip("type params are not enabled") - } for _, src := range invalids { checkErrors(t, src, src, DeclarationErrors|AllErrors, true) } diff --git a/libgo/go/go/parser/testdata/linalg.go2 b/libgo/go/go/parser/testdata/linalg.go2 index fba0d02..7ccb19c 100644 --- a/libgo/go/go/parser/testdata/linalg.go2 +++ b/libgo/go/go/parser/testdata/linalg.go2 @@ -9,10 +9,10 @@ import "math" // Numeric is type bound that matches any numeric type. // It would likely be in a constraints package in the standard library. type Numeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - complex64, complex128 + ~int|~int8|~int16|~int32|~int64| + ~uint|~uint8|~uint16|~uint32|~uint64|~uintptr| + ~float32|~float64| + ~complex64|~complex128 } func DotProduct[T Numeric](s1, s2 []T) T { @@ -42,14 +42,14 @@ func AbsDifference[T NumericAbs](a, b T) T { // OrderedNumeric is a type bound that matches numeric types that support the < operator. type OrderedNumeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64 + ~int|~int8|~int16|~int32|~int64| + ~uint|~uint8|~uint16|~uint32|~uint64|~uintptr| + ~float32|~float64 } // Complex is a type bound that matches the two complex types, which do not have a < operator. type Complex interface { - type complex64, complex128 + ~complex64|~complex128 } // OrderedAbs is a helper type that defines an Abs method for diff --git a/libgo/go/go/parser/testdata/resolution/typeparams.go2 b/libgo/go/go/parser/testdata/resolution/typeparams.go2 index 0ffecd6..8c243af 100644 --- a/libgo/go/go/parser/testdata/resolution/typeparams.go2 +++ b/libgo/go/go/parser/testdata/resolution/typeparams.go2 @@ -15,7 +15,7 @@ type Pair /* =@Pair */ [L /* =@L */, R /* =@R */ any] struct { var _ = Pair /* @Pair */ [int, string]{} type Addable /* =@Addable */ interface { - type int64, float64 + ~int64|~float64 } func Add /* =@AddDecl */[T /* =@T */ Addable /* @Addable */](l /* =@l */, r /* =@r */ T /* @T */) T /* @T */ { @@ -30,7 +30,7 @@ type Receiver /* =@Receiver */[P /* =@P */ any] struct {} // parameter below. func (r /* =@recv */ Receiver /* @Receiver */ [P]) m() P {} -func f /* =@f */[T1 /* =@T1 */ interface{type []T2 /* @T2 */}, T2 /* =@T2 */ any]( +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: // the duplicate T1 should // not be allowed. diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 913281e..19d4ab6 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -11,7 +11,6 @@ package printer import ( "bytes" "go/ast" - "go/internal/typeparams" "go/token" "math" "strconv" @@ -383,8 +382,8 @@ func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) { } func (p *printer) signature(sig *ast.FuncType) { - if tparams := typeparams.Get(sig); tparams != nil { - p.parameters(tparams, true) + if sig.TypeParams != nil { + p.parameters(sig.TypeParams, true) } if sig.Params != nil { p.parameters(sig.Params, false) @@ -472,17 +471,9 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) p.expr(f.Type) } else { // interface if len(f.Names) > 0 { - // type list type or method - name := f.Names[0] // "type" or method name + name := f.Names[0] // method name p.expr(name) - if name.Name == "type" { - // type list type - p.print(blank) - p.expr(f.Type) - } else { - // method - p.signature(f.Type.(*ast.FuncType)) // don't print "func" - } + p.signature(f.Type.(*ast.FuncType)) // don't print "func" } else { // embedded interface p.expr(f.Type) @@ -569,24 +560,10 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) p.setComment(f.Doc) p.recordLine(&line) if name != nil { - // type list type or method - if name.Name == "type" { - // type list type - if name == prev { - // type is part of a list of types - p.print(token.COMMA, blank) - } else { - // type starts a new list of types - p.print(name, blank) - } - p.expr(f.Type) - prev = name - } else { - // method - p.expr(name) - p.signature(f.Type.(*ast.FuncType)) // don't print "func" - prev = nil - } + // method + p.expr(name) + p.signature(f.Type.(*ast.FuncType)) // don't print "func" + prev = nil } else { // embedded interface p.expr(f.Type) @@ -871,17 +848,15 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1) p.print(x.Lbrack, token.LBRACK) - // Note: we're a bit defensive here to handle the case of a ListExpr of - // length 1. - if list := typeparams.UnpackExpr(x.Index); len(list) > 0 { - if len(list) > 1 { - p.exprList(x.Lbrack, list, depth+1, commaTerm, x.Rbrack, false) - } else { - p.expr0(list[0], depth+1) - } - } else { - p.expr0(x.Index, depth+1) - } + p.expr0(x.Index, depth+1) + p.print(x.Rbrack, token.RBRACK) + + case *ast.IndexListExpr: + // TODO(gri): as for IndexExpr, should treat [] like parentheses and undo + // one level of depth + p.expr1(x.X, token.HighestPrec, 1) + p.print(x.Lbrack, token.LBRACK) + p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false) p.print(x.Rbrack, token.RBRACK) case *ast.SliceExpr: @@ -1052,7 +1027,7 @@ func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit { break } // remove leading 0's from integer (but not floating-point) imaginary literals - if x[len(x)-1] == 'i' && strings.IndexByte(x, '.') < 0 && strings.IndexByte(x, 'e') < 0 { + if x[len(x)-1] == 'i' && !strings.ContainsAny(x, ".e") { x = strings.TrimLeft(x, "0_") if x == "i" { x = "0i" @@ -1635,8 +1610,8 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { case *ast.TypeSpec: p.setComment(s.Doc) p.expr(s.Name) - if tparams := typeparams.Get(s); tparams != nil { - p.parameters(tparams, true) + if s.TypeParams != nil { + p.parameters(s.TypeParams, true) } if n == 1 { p.print(blank) diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index ba61f78..e4679b0 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -104,7 +104,7 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node] p.cachedPos = -1 } -func (p *printer) internalError(msg ...interface{}) { +func (p *printer) internalError(msg ...any) { if debug { fmt.Print(p.pos.String() + ": ") fmt.Println(msg...) @@ -559,12 +559,9 @@ func stripCommonPrefix(lines []string) { * Check for vertical "line of stars" and correct prefix accordingly. */ lineOfStars := false - if i := strings.Index(prefix, "*"); i >= 0 { - // Line of stars present. - if i > 0 && prefix[i-1] == ' ' { - i-- // remove trailing blank from prefix so stars remain aligned - } - prefix = prefix[0:i] + if p, _, ok := strings.Cut(prefix, "*"); ok { + // remove trailing blank from prefix so stars remain aligned + prefix = strings.TrimSuffix(p, " ") lineOfStars = true } else { // No line of stars present. @@ -616,8 +613,8 @@ func stripCommonPrefix(lines []string) { // lines. last := lines[len(lines)-1] closing := "*/" - i := strings.Index(last, closing) // i >= 0 (closing is always present) - if isBlank(last[0:i]) { + before, _, _ := strings.Cut(last, closing) // closing always present + if isBlank(before) { // last line only contains closing */ if lineOfStars { closing = " */" // add blank to align final star @@ -881,7 +878,7 @@ func mayCombine(prev token.Token, next byte) (b bool) { // space for best comment placement. Then, any leftover whitespace is // printed, followed by the actual token. // -func (p *printer) print(args ...interface{}) { +func (p *printer) print(args ...any) { for _, arg := range args { // information about the current arg var data string @@ -1078,7 +1075,7 @@ func getLastComment(n ast.Node) *ast.CommentGroup { return nil } -func (p *printer) printNode(node interface{}) error { +func (p *printer) printNode(node any) error { // unpack *CommentedNode, if any var comments []*ast.CommentGroup if cnode, ok := node.(*CommentedNode); ok { @@ -1312,7 +1309,7 @@ type Config struct { } // fprint implements Fprint and takes a nodesSizes map for setting up the printer state. -func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) { +func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeSizes map[ast.Node]int) (err error) { // print node var p printer p.init(cfg, fset, nodeSizes) @@ -1368,7 +1365,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{ // It may be provided as argument to any of the Fprint functions. // type CommentedNode struct { - Node interface{} // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt + Node any // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt Comments []*ast.CommentGroup } @@ -1377,7 +1374,7 @@ type CommentedNode struct { // The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, // or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. // -func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { +func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) } @@ -1386,6 +1383,6 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{ // Note that gofmt uses tabs for indentation but spaces for alignment; // use format.Node (package go/format) for output that matches gofmt. // -func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { +func Fprint(output io.Writer, fset *token.FileSet, node any) error { return (&Config{Tabwidth: 8}).Fprint(output, fset, node) } diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index 20c97b8..ff8be4a 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -10,7 +10,6 @@ import ( "flag" "fmt" "go/ast" - "go/internal/typeparams" "go/parser" "go/token" "io" @@ -222,9 +221,6 @@ var data = []entry{ func TestFiles(t *testing.T) { t.Parallel() for _, e := range data { - if !typeparams.Enabled && e.mode&allowTypeParams != 0 { - continue - } source := filepath.Join(dataDir, e.source) golden := filepath.Join(dataDir, e.golden) mode := e.mode diff --git a/libgo/go/go/printer/testdata/generics.golden b/libgo/go/go/printer/testdata/generics.golden index cc7fbbe..3d95eda 100644 --- a/libgo/go/go/printer/testdata/generics.golden +++ b/libgo/go/go/printer/testdata/generics.golden @@ -22,7 +22,7 @@ func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} func f[P interface{}](x P) func f[P1, P2, P3 interface { m1(P1) - type P2, P3 + ~P2 | ~P3 }](x1 P1, x2 P2, x3 P3) struct{} func f[P any](T1[P], T2[P]) T3[P] @@ -35,9 +35,6 @@ func _() { _ = []T[P]{} } -// properly format one-line type lists -type _ interface{ type a } - -type _ interface { - type a, b, c -} +// type constraint literals with elided interfaces +func _[P ~int, Q int | string]() {} +func _[P struct{ f int }, Q *P]() {} diff --git a/libgo/go/go/printer/testdata/generics.input b/libgo/go/go/printer/testdata/generics.input index f4571ad..746dfdd 100644 --- a/libgo/go/go/printer/testdata/generics.input +++ b/libgo/go/go/printer/testdata/generics.input @@ -20,7 +20,7 @@ func f[P any](x P) func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} func f[P interface{}](x P) -func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{} +func f[P1, P2, P3 interface{ m1(P1); ~P2|~P3 }](x1 P1, x2 P2, x3 P3) struct{} func f[P any](T1[P], T2[P]) T3[P] func (x T[P]) m() @@ -32,7 +32,6 @@ func _() { _ = []T[P]{} } -// properly format one-line type lists -type _ interface { type a } - -type _ interface { type a,b,c } +// type constraint literals with elided interfaces +func _[P ~int, Q int | string]() {} +func _[P struct{f int}, Q *P]() {} diff --git a/libgo/go/go/printer/testdata/parser.go b/libgo/go/go/printer/testdata/parser.go index fc2812a..7e83797 100644 --- a/libgo/go/go/printer/testdata/parser.go +++ b/libgo/go/go/printer/testdata/parser.go @@ -122,7 +122,7 @@ func (p *parser) closeLabelScope() { p.labelScope = p.labelScope.Outer } -func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { +func (p *parser) declare(decl 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.Name != "_" { @@ -200,7 +200,7 @@ func (p *parser) resolve(x ast.Expr) { // ---------------------------------------------------------------------------- // Parsing support -func (p *parser) printTrace(a ...interface{}) { +func (p *parser) printTrace(a ...any) { const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " const n = uint(len(dots)) diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go index f08e28c..23d8db9 100644 --- a/libgo/go/go/scanner/scanner.go +++ b/libgo/go/go/scanner/scanner.go @@ -155,7 +155,7 @@ func (s *Scanner) error(offs int, msg string) { s.ErrorCount++ } -func (s *Scanner) errorf(offs int, format string, args ...interface{}) { +func (s *Scanner) errorf(offs int, format string, args ...any) { s.error(offs, fmt.Sprintf(format, args...)) } @@ -969,6 +969,8 @@ scanAgain: } case '|': tok = s.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR) + case '~': + tok = token.TILDE default: // next reports unexpected BOMs - don't repeat if ch != bom { diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go index db123c3..de45e16 100644 --- a/libgo/go/go/scanner/scanner_test.go +++ b/libgo/go/go/scanner/scanner_test.go @@ -40,7 +40,7 @@ type elt struct { class int } -var tokens = [...]elt{ +var tokens = []elt{ // Special tokens {token.COMMENT, "/* a comment */", special}, {token.COMMENT, "// a comment \n", special}, @@ -149,6 +149,7 @@ var tokens = [...]elt{ {token.RBRACE, "}", operator}, {token.SEMICOLON, ";", operator}, {token.COLON, ":", operator}, + {token.TILDE, "~", operator}, // Keywords {token.BREAK, "break", keyword}, diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go index 0d7982c..ce4af03 100644 --- a/libgo/go/go/token/position.go +++ b/libgo/go/go/token/position.go @@ -540,7 +540,7 @@ func searchInts(a []int, x int) int { // TODO(gri): Remove this when compilers have caught up. i, j := 0, len(a) for i < j { - h := i + (j-i)>>1 // avoid overflow when computing h + h := int(uint(i+j) >> 1) // avoid overflow when computing h // i ≤ h < j if a[h] <= x { i = h + 1 diff --git a/libgo/go/go/token/serialize.go b/libgo/go/go/token/serialize.go index d0ea345..ffb6990 100644 --- a/libgo/go/go/token/serialize.go +++ b/libgo/go/go/token/serialize.go @@ -19,7 +19,7 @@ type serializedFileSet struct { } // Read calls decode to deserialize a file set into s; s must not be nil. -func (s *FileSet) Read(decode func(interface{}) error) error { +func (s *FileSet) Read(decode func(any) error) error { var ss serializedFileSet if err := decode(&ss); err != nil { return err @@ -47,7 +47,7 @@ func (s *FileSet) Read(decode func(interface{}) error) error { } // Write calls encode to serialize the file set s. -func (s *FileSet) Write(encode func(interface{}) error) error { +func (s *FileSet) Write(encode func(any) error) error { var ss serializedFileSet s.mutex.Lock() diff --git a/libgo/go/go/token/serialize_test.go b/libgo/go/go/token/serialize_test.go index 4e925adb..4aa0b0d 100644 --- a/libgo/go/go/token/serialize_test.go +++ b/libgo/go/go/token/serialize_test.go @@ -70,7 +70,7 @@ func equal(p, q *FileSet) error { func checkSerialize(t *testing.T, p *FileSet) { var buf bytes.Buffer - encode := func(x interface{}) error { + encode := func(x any) error { return gob.NewEncoder(&buf).Encode(x) } if err := p.Write(encode); err != nil { @@ -78,7 +78,7 @@ func checkSerialize(t *testing.T, p *FileSet) { return } q := NewFileSet() - decode := func(x interface{}) error { + decode := func(x any) error { return gob.NewDecoder(&buf).Decode(x) } if err := q.Read(decode); err != nil { diff --git a/libgo/go/go/token/token.go b/libgo/go/go/token/token.go index 96a1079..d22e575 100644 --- a/libgo/go/go/token/token.go +++ b/libgo/go/go/token/token.go @@ -125,6 +125,11 @@ const ( TYPE VAR keyword_end + + additional_beg + // additional tokens, handled in an ad-hoc manner + TILDE + additional_end ) var tokens = [...]string{ @@ -225,6 +230,8 @@ var tokens = [...]string{ SWITCH: "switch", TYPE: "type", VAR: "var", + + TILDE: "~", } // String returns the string corresponding to the token tok. @@ -304,7 +311,9 @@ func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_en // IsOperator returns true for tokens corresponding to operators and // delimiters; it returns false otherwise. // -func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } +func (tok Token) IsOperator() bool { + return (operator_beg < tok && tok < operator_end) || tok == TILDE +} // IsKeyword returns true for tokens corresponding to keywords; // it returns false otherwise. diff --git a/libgo/go/go/types/api.go b/libgo/go/go/types/api.go index d645b48..f0a2280 100644 --- a/libgo/go/go/types/api.go +++ b/libgo/go/go/types/api.go @@ -35,6 +35,8 @@ import ( _ "unsafe" // for go:linkname ) +const allowTypeLists = false + // An Error describes a type-checking error; it implements the error interface. // A "soft" error is an error that still permits a valid interpretation of a // package (such as "unused variable"); "hard" errors may lead to unpredictable @@ -61,6 +63,15 @@ func (err Error) Error() string { return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) } +// An ArgumentError holds an error associated with an argument index. +type ArgumentError struct { + Index int + Err error +} + +func (e *ArgumentError) Error() string { return e.Err.Error() } +func (e *ArgumentError) Unwrap() error { return e.Err } + // An Importer resolves import paths to Packages. // // CAUTION: This interface does not support the import of locally @@ -102,12 +113,16 @@ type ImporterFrom interface { // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { - // goVersion describes the accepted Go language version. The string + // Context is the context used for resolving global identifiers. If nil, the + // type checker will initialize this field with a newly created context. + Context *Context + + // GoVersion describes the accepted Go language version. The string // must follow the format "go%d.%d" (e.g. "go1.12") or it must be // empty; an empty string indicates the latest language version. // If the format is invalid, invoking the type checker will cause a // panic. - goVersion string + GoVersion string // If IgnoreFuncBodies is set, function bodies are not // type-checked. @@ -162,7 +177,110 @@ func srcimporter_setUsesCgo(conf *Config) { conf.go115UsesCgo = true } -// The Info struct is found in api_notypeparams.go and api_typeparams.go. +// Info holds result type information for a type-checked package. +// Only the information for which a map is provided is collected. +// If the package has type errors, the collected information may +// be incomplete. +type Info struct { + // Types maps expressions to their types, and for constant + // expressions, also their values. Invalid expressions are + // omitted. + // + // For (possibly parenthesized) identifiers denoting built-in + // functions, the recorded signatures are call-site specific: + // if the call result is not a constant, the recorded type is + // an argument-specific signature. Otherwise, the recorded type + // is invalid. + // + // The Types map does not record the type of every identifier, + // only those that appear where an arbitrary expression is + // permitted. For instance, the identifier f in a selector + // expression x.f is found only in the Selections map, the + // identifier z in a variable declaration 'var z int' is found + // only in the Defs map, and identifiers denoting packages in + // qualified identifiers are collected in the Uses map. + Types map[ast.Expr]TypeAndValue + + // Instances maps identifiers denoting parameterized types or functions to + // their type arguments and instantiated type. + // + // For example, Instances will map the identifier for 'T' in the type + // instantiation T[int, string] to the type arguments [int, string] and + // resulting instantiated *Named type. Given a parameterized function + // func F[A any](A), Instances will map the identifier for 'F' in the call + // expression F(int(1)) to the inferred type arguments [int], and resulting + // instantiated *Signature. + // + // Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs + // results in an equivalent of Instances[id].Type. + Instances map[*ast.Ident]Instance + + // Defs maps identifiers to the objects they define (including + // package names, dots "." of dot-imports, and blank "_" identifiers). + // For identifiers that do not denote objects (e.g., the package name + // in package clauses, or symbolic variables t in t := x.(type) of + // type switch headers), the corresponding objects are nil. + // + // For an embedded field, Defs returns the field *Var it defines. + // + // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos() + Defs map[*ast.Ident]Object + + // Uses maps identifiers to the objects they denote. + // + // For an embedded field, Uses returns the *TypeName it denotes. + // + // Invariant: Uses[id].Pos() != id.Pos() + Uses map[*ast.Ident]Object + + // Implicits maps nodes to their implicitly declared objects, if any. + // The following node and object types may appear: + // + // node declared object + // + // *ast.ImportSpec *PkgName for imports without renames + // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) + // *ast.Field anonymous parameter *Var (incl. unnamed results) + // + Implicits map[ast.Node]Object + + // Selections maps selector expressions (excluding qualified identifiers) + // to their corresponding selections. + Selections map[*ast.SelectorExpr]*Selection + + // Scopes maps ast.Nodes to the scopes they define. Package scopes are not + // associated with a specific node but with all files belonging to a package. + // Thus, the package scope can be found in the type-checked Package object. + // Scopes nest, with the Universe scope being the outermost scope, enclosing + // the package scope, which contains (one or more) files scopes, which enclose + // function scopes which in turn enclose statement and function literal scopes. + // Note that even though package-level functions are declared in the package + // scope, the function scopes are embedded in the file scope of the file + // containing the function declaration. + // + // The following node types may appear in Scopes: + // + // *ast.File + // *ast.FuncType + // *ast.TypeSpec + // *ast.BlockStmt + // *ast.IfStmt + // *ast.SwitchStmt + // *ast.TypeSwitchStmt + // *ast.CaseClause + // *ast.CommClause + // *ast.ForStmt + // *ast.RangeStmt + // + Scopes map[ast.Node]*Scope + + // InitOrder is the list of package-level initializers in the order in which + // they must be executed. Initializers referring to variables related by an + // initialization dependency appear in topological order, the others appear + // in source order. Variables without an initialization expression do not + // appear in this list. + InitOrder []*Initializer +} // TypeOf returns the type of expression e, or nil if not found. // Precondition: the Types, Uses and Defs maps are populated. @@ -254,11 +372,13 @@ func (tv TypeAndValue) HasOk() bool { return tv.mode == commaok || tv.mode == mapindex } -// _Inferred reports the _Inferred type arguments and signature -// for a parameterized function call that uses type inference. -type _Inferred struct { - Targs []Type - Sig *Signature +// Instance reports the type arguments and instantiated type for type and +// function instantiations. For type instantiations, Type will be of dynamic +// type *Named. For function instantiations, Type will be of dynamic type +// *Signature. +type Instance struct { + TypeArgs *TypeList + Type Type } // An Initializer describes a package-level variable, or a list of variables in case @@ -319,18 +439,26 @@ func ConvertibleTo(V, T Type) bool { // Implements reports whether type V implements interface T. func Implements(V Type, T *Interface) bool { - f, _ := MissingMethod(V, T, true) - return f == nil + if T.Empty() { + // All types (even Typ[Invalid]) implement the empty interface. + return true + } + // Checker.implements suppresses errors for invalid types, so we need special + // handling here. + if V.Underlying() == Typ[Invalid] { + return false + } + return (*Checker)(nil).implements(V, T) == nil } // Identical reports whether x and y are identical types. // Receivers of Signature types are ignored. func Identical(x, y Type) bool { - return (*Checker)(nil).identical(x, y) + return identical(x, y, true, nil) } // IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored. // Receivers of Signature types are ignored. func IdenticalIgnoreTags(x, y Type) bool { - return (*Checker)(nil).identicalIgnoreTags(x, y) + return identical(x, y, false, nil) } diff --git a/libgo/go/go/types/api_notypeparams.go b/libgo/go/go/types/api_notypeparams.go deleted file mode 100644 index 9f7cb7e..0000000 --- a/libgo/go/go/types/api_notypeparams.go +++ /dev/null @@ -1,104 +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. - -//go:build !typeparams -// +build !typeparams - -package types - -import "go/ast" - -// Info holds result type information for a type-checked package. -// Only the information for which a map is provided is collected. -// If the package has type errors, the collected information may -// be incomplete. -type Info struct { - // Types maps expressions to their types, and for constant - // expressions, also their values. Invalid expressions are - // omitted. - // - // For (possibly parenthesized) identifiers denoting built-in - // functions, the recorded signatures are call-site specific: - // if the call result is not a constant, the recorded type is - // an argument-specific signature. Otherwise, the recorded type - // is invalid. - // - // The Types map does not record the type of every identifier, - // only those that appear where an arbitrary expression is - // permitted. For instance, the identifier f in a selector - // expression x.f is found only in the Selections map, the - // identifier z in a variable declaration 'var z int' is found - // only in the Defs map, and identifiers denoting packages in - // qualified identifiers are collected in the Uses map. - Types map[ast.Expr]TypeAndValue - - // Defs maps identifiers to the objects they define (including - // package names, dots "." of dot-imports, and blank "_" identifiers). - // For identifiers that do not denote objects (e.g., the package name - // in package clauses, or symbolic variables t in t := x.(type) of - // type switch headers), the corresponding objects are nil. - // - // For an embedded field, Defs returns the field *Var it defines. - // - // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos() - Defs map[*ast.Ident]Object - - // Uses maps identifiers to the objects they denote. - // - // For an embedded field, Uses returns the *TypeName it denotes. - // - // Invariant: Uses[id].Pos() != id.Pos() - Uses map[*ast.Ident]Object - - // Implicits maps nodes to their implicitly declared objects, if any. - // The following node and object types may appear: - // - // node declared object - // - // *ast.ImportSpec *PkgName for imports without renames - // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) - // *ast.Field anonymous parameter *Var (incl. unnamed results) - // - Implicits map[ast.Node]Object - - // Selections maps selector expressions (excluding qualified identifiers) - // to their corresponding selections. - Selections map[*ast.SelectorExpr]*Selection - - // Scopes maps ast.Nodes to the scopes they define. Package scopes are not - // associated with a specific node but with all files belonging to a package. - // Thus, the package scope can be found in the type-checked Package object. - // Scopes nest, with the Universe scope being the outermost scope, enclosing - // the package scope, which contains (one or more) files scopes, which enclose - // function scopes which in turn enclose statement and function literal scopes. - // Note that even though package-level functions are declared in the package - // scope, the function scopes are embedded in the file scope of the file - // containing the function declaration. - // - // The following node types may appear in Scopes: - // - // *ast.File - // *ast.FuncType - // *ast.BlockStmt - // *ast.IfStmt - // *ast.SwitchStmt - // *ast.TypeSwitchStmt - // *ast.CaseClause - // *ast.CommClause - // *ast.ForStmt - // *ast.RangeStmt - // - Scopes map[ast.Node]*Scope - - // InitOrder is the list of package-level initializers in the order in which - // they must be executed. Initializers referring to variables related by an - // initialization dependency appear in topological order, the others appear - // in source order. Variables without an initialization expression do not - // appear in this list. - InitOrder []*Initializer -} - -func getInferred(info *Info) map[ast.Expr]_Inferred { - return nil -} diff --git a/libgo/go/go/types/api_test.go b/libgo/go/go/types/api_test.go index 0ff8da6..09d2861 100644 --- a/libgo/go/go/types/api_test.go +++ b/libgo/go/go/types/api_test.go @@ -6,6 +6,7 @@ package types_test import ( "bytes" + "errors" "fmt" "go/ast" "go/importer" @@ -27,8 +28,12 @@ import ( // If source begins with "package generic_" and type parameters are enabled, // generic code is permitted. func pkgFor(path, source string, info *Info) (*Package, error) { - fset := token.NewFileSet() mode := modeForSource(source) + return pkgForMode(path, source, info, mode) +} + +func pkgForMode(path, source string, info *Info, mode parser.Mode) (*Package, error) { + fset := token.NewFileSet() f, err := parser.ParseFile(fset, path, source, mode) if err != nil { return nil, err @@ -123,7 +128,6 @@ func TestValuesInfo(t *testing.T) { {`package c5d; var _ = string(65)`, `65`, `untyped int`, `65`}, {`package c5e; var _ = string('A')`, `'A'`, `untyped rune`, `65`}, {`package c5f; type T string; var _ = T('A')`, `'A'`, `untyped rune`, `65`}, - {`package c5g; var s uint; var _ = string(1 << s)`, `1 << s`, `untyped int`, ``}, {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`}, {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`}, @@ -153,6 +157,7 @@ func TestValuesInfo(t *testing.T) { {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`}, {`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341 + {`package g1; var(j int32; s int; n = 1.0<<s == j)`, `1.0`, `int32`, `1`}, // issue #48422 } for _, test := range tests { @@ -341,19 +346,21 @@ func TestTypesInfo(t *testing.T) { {broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`}, {broken + `x3; var x = panic("");`, `panic`, `func(interface{})`}, {`package x4; func _() { panic("") }`, `panic`, `func(interface{})`}, - {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`}, + {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`}, // parameterized functions - {genericPkg + `p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ interface{}](T₁)`}, - {genericPkg + `p1; func f[T any](T); var _ = f[int]`, `f[int]`, `func(int)`}, - {genericPkg + `p2; func f[T any](T); func _() { f(42) }`, `f`, `func[T₁ interface{}](T₁)`}, - {genericPkg + `p3; func f[T any](T); func _() { f(42) }`, `f(42)`, `()`}, + {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T any](T)`}, + {genericPkg + `p1; func f[T any](T) {}; var _ = f[int]`, `f[int]`, `func(int)`}, + {genericPkg + `p2; func f[T any](T) {}; func _() { f(42) }`, `f`, `func(int)`}, + {genericPkg + `p3; func f[T any](T) {}; func _() { f[int](42) }`, `f[int]`, `func(int)`}, + {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T any](T)`}, + {genericPkg + `p5; func f[T any](T) {}; func _() { f(42) }`, `f(42)`, `()`}, // type parameters {genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t - {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P₁ interface{}]`}, - {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P₁ interface{}]`}, - {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P₁, Q₂ interface{}]`}, + {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P any]`}, + {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P interface{}]`}, + {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P, Q interface{}]`}, // TODO (rFindley): compare with types2, which resolves the type broken_t4.t[P₁, Q₂ interface{m()}] here {broken + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t`}, @@ -362,14 +369,38 @@ func TestTypesInfo(t *testing.T) { {genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`}, // issue 45096 - {genericPkg + `issue45096; func _[T interface{ type int8, int16, int32 }](x T) { _ = x < 0 }`, `0`, `T₁`}, + {genericPkg + `issue45096; func _[T interface{ ~int8 | ~int16 | ~int32 }](x T) { _ = x < 0 }`, `0`, `T`}, + + // issue 47895 + {`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`}, + + // issue 50093 + {genericPkg + `u0a; func _[_ interface{int}]() {}`, `int`, `int`}, + {genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`}, + {genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`}, + {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`}, + {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string`, `int|string`}, + {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`}, + {genericPkg + `u3a; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`}, + + {genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`}, + {genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`}, + {genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`}, + {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`}, + {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`}, + {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`}, + {genericPkg + `u3b; func _[_ int|string|~float64|~bool]() {}`, `int | string | ~float64`, `int|string|~float64`}, + + {genericPkg + `u0c; type _ interface{int}`, `int`, `int`}, + {genericPkg + `u1c; type _ interface{~int}`, `~int`, `~int`}, + {genericPkg + `u2c; type _ interface{int|string}`, `int | string`, `int|string`}, + {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`}, + {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string`, `int|string`}, + {genericPkg + `u3c; type _ interface{int|string|~bool}`, `~bool`, `~bool`}, + {genericPkg + `u3c; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`}, } for _, test := range tests { - ResetId() // avoid renumbering of type parameter ids when adding tests - if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled { - continue - } info := Info{Types: make(map[ast.Expr]TypeAndValue)} var name string if strings.HasPrefix(test.src, broken) { @@ -403,6 +434,193 @@ func TestTypesInfo(t *testing.T) { } } +func TestInstanceInfo(t *testing.T) { + var tests = []struct { + src string + name string + targs []string + typ string + }{ + {`package p0; func f[T any](T) {}; func _() { f(42) }`, + `f`, + []string{`int`}, + `func(int)`, + }, + {`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`, + `f`, + []string{`rune`}, + `func(rune) rune`, + }, + {`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`, + `f`, + []string{`complex128`}, + `func(...complex128) complex128`, + }, + {`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`, + `f`, + []string{`float64`, `string`, `byte`}, + `func(float64, *string, []byte)`, + }, + {`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`, + `f`, + []string{`float64`, `byte`}, + `func(float64, *byte, ...[]byte)`, + }, + + {`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`, + `f`, + []string{`string`, `*string`}, + `func(x string)`, + }, + {`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`, + `f`, + []string{`int`, `*int`}, + `func(x []int)`, + }, + {`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`, + `f`, + []string{`int`, `chan<- int`}, + `func(x []int)`, + }, + {`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`, + `f`, + []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, + `func(x []int)`, + }, + + {`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`, + `f`, + []string{`string`, `*string`}, + `func() string`, + }, + {`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`, + `f`, + []string{`string`, `*string`}, + `func() string`, + }, + {`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`, + `f`, + []string{`int`, `chan<- int`}, + `func() []int`, + }, + {`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`, + `f`, + []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, + `func() []int`, + }, + {`package i0; import "lib"; func _() { lib.F(42) }`, + `F`, + []string{`int`}, + `func(int)`, + }, + {`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`, + `T`, + []string{`int`}, + `struct{x int}`, + }, + {`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`, + `T`, + []string{`int`}, + `struct{x int}`, + }, + {`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`, + `T`, + []string{`int`}, + `struct{x int}`, + }, + {`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`, + `T`, + []string{`[]int`, `int`}, + `struct{x []int; y int}`, + }, + {`package type4; import "lib"; var _ lib.T[int]`, + `T`, + []string{`int`}, + `[]int`, + }, + } + + for _, test := range tests { + const lib = `package lib + +func F[P any](P) {} + +type T[P any] []P +` + + imports := make(testImporter) + conf := Config{Importer: imports} + instances := make(map[*ast.Ident]Instance) + uses := make(map[*ast.Ident]Object) + makePkg := func(src string) *Package { + f, err := parser.ParseFile(fset, "p.go", src, 0) + if err != nil { + t.Fatal(err) + } + pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instances, Uses: uses}) + if err != nil { + t.Fatal(err) + } + imports[pkg.Name()] = pkg + return pkg + } + makePkg(lib) + pkg := makePkg(test.src) + + // look for instance information + var targs []Type + var typ Type + for ident, inst := range instances { + if ExprString(ident) == test.name { + for i := 0; i < inst.TypeArgs.Len(); i++ { + targs = append(targs, inst.TypeArgs.At(i)) + } + typ = inst.Type + + // Check that we can find the corresponding parameterized type. + ptype := uses[ident].Type() + lister, _ := ptype.(interface{ TypeParams() *TypeParamList }) + if lister == nil || lister.TypeParams().Len() == 0 { + t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype) + continue + } + + // Verify the invariant that re-instantiating the generic type with + // TypeArgs results in an equivalent type. + inst2, err := Instantiate(nil, ptype, targs, true) + if err != nil { + t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err) + } + if !Identical(inst.Type, inst2) { + t.Errorf("%v and %v are not identical", inst.Type, inst2) + } + break + } + } + if targs == nil { + t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name) + continue + } + + // check that type arguments are correct + if len(targs) != len(test.targs) { + t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs)) + continue + } + for i, targ := range targs { + if got := targ.String(); got != test.targs[i] { + t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i]) + continue + } + } + + // check that the types match + if got := typ.Underlying().String(); got != test.typ { + t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ) + } + } +} + func TestDefsInfo(t *testing.T) { var tests = []struct { src string @@ -416,18 +634,13 @@ func TestDefsInfo(t *testing.T) { {`package p4; func f()`, `f`, `func p4.f()`}, {`package p5; func f() int { x, _ := 1, 2; return x }`, `_`, `var _ int`}, - // generic types must be sanitized - // (need to use sufficiently nested types to provoke unexpanded types) - {genericPkg + `g0; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`}, - {genericPkg + `g1; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`}, - {genericPkg + `g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`}, - {genericPkg + `g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var generic_g3.g func(x struct{f generic_g3.t[string]})`}, + // Tests using generics. + {`package generic_g0; type x[T any] int`, `x`, `type generic_g0.x[T any] int`}, + {`package generic_g1; func f[T any]() {}`, `f`, `func generic_g1.f[T any]()`}, + {`package generic_g2; type x[T any] int; func (*x[_]) m() {}`, `m`, `func (*generic_g2.x[_]).m()`}, } for _, test := range tests { - if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled { - continue - } info := Info{ Defs: make(map[*ast.Ident]Object), } @@ -464,18 +677,35 @@ func TestUsesInfo(t *testing.T) { {`package p3; func _() { type _ x }; type x int`, `x`, `type p3.x int`}, {`package p4; func _() { _ = f }; func f()`, `f`, `func p4.f()`}, - // generic types must be sanitized - // (need to use sufficiently nested types to provoke unexpanded types) - {genericPkg + `g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`}, - {genericPkg + `g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`}, - {genericPkg + `g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`}, - {genericPkg + `g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func generic_g3.f(x struct{f generic_g3.t[string]})`}, + // Tests using generics. + {`package generic_g0; func _[T any]() { _ = x }; const x = 42`, `x`, `const generic_g0.x untyped int`}, + {`package generic_g1; func _[T any](x T) { }`, `T`, `type parameter T any`}, + {`package generic_g2; type N[A any] int; var _ N[int]`, `N`, `type generic_g2.N[A any] int`}, + {`package generic_g3; type N[A any] int; func (N[_]) m() {}`, `N`, `type generic_g3.N[A any] int`}, + + // Uses of fields are instantiated. + {`package generic_s1; type N[A any] struct{ a A }; var f = N[int]{}.a`, `a`, `field a int`}, + {`package generic_s1; type N[A any] struct{ a A }; func (r N[B]) m(b B) { r.a = b }`, `a`, `field a B`}, + + // Uses of methods are uses of the instantiated method. + {`package generic_m0; type N[A any] int; func (r N[B]) m() { r.n() }; func (N[C]) n() {}`, `n`, `func (generic_m0.N[B]).n()`}, + {`package generic_m1; type N[A any] int; func (r N[B]) m() { }; var f = N[int].m`, `m`, `func (generic_m1.N[int]).m()`}, + {`package generic_m2; func _[A any](v interface{ m() A }) { v.m() }`, `m`, `func (interface).m() A`}, + {`package generic_m3; func f[A any]() interface{ m() A } { return nil }; var _ = f[int]().m()`, `m`, `func (interface).m() int`}, + {`package generic_m4; type T[A any] func() interface{ m() A }; var x T[int]; var y = x().m`, `m`, `func (interface).m() int`}, + {`package generic_m5; type T[A any] interface{ m() A }; func _[B any](t T[B]) { t.m() }`, `m`, `func (generic_m5.T[B]).m() B`}, + {`package generic_m6; type T[A any] interface{ m() }; func _[B any](t T[B]) { t.m() }`, `m`, `func (generic_m6.T[B]).m()`}, + {`package generic_m7; type T[A any] interface{ m() A }; func _(t T[int]) { t.m() }`, `m`, `func (generic_m7.T[int]).m() int`}, + {`package generic_m8; type T[A any] interface{ m() }; func _(t T[int]) { t.m() }`, `m`, `func (generic_m8.T[int]).m()`}, + {`package generic_m9; type T[A any] interface{ m() }; func _(t T[int]) { _ = t.m }`, `m`, `func (generic_m9.T[int]).m()`}, + { + `package generic_m10; type E[A any] interface{ m() }; type T[B any] interface{ E[B]; n() }; func _(t T[int]) { t.m() }`, + `m`, + `func (generic_m10.E[int]).m()`, + }, } for _, test := range tests { - if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled { - continue - } info := Info{ Uses: make(map[*ast.Ident]Object), } @@ -485,8 +715,10 @@ func TestUsesInfo(t *testing.T) { var use Object for id, obj := range info.Uses { if id.Name == test.obj { + if use != nil { + panic(fmt.Sprintf("multiple uses of %q", id.Name)) + } use = obj - break } } if use == nil { @@ -500,6 +732,90 @@ func TestUsesInfo(t *testing.T) { } } +func TestGenericMethodInfo(t *testing.T) { + src := `package p + +type N[A any] int + +func (r N[B]) m() { r.m(); r.n() } + +func (r *N[C]) n() { } +` + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "p.go", src, 0) + if err != nil { + t.Fatal(err) + } + info := Info{ + Defs: make(map[*ast.Ident]Object), + Uses: make(map[*ast.Ident]Object), + Selections: make(map[*ast.SelectorExpr]*Selection), + } + var conf Config + pkg, err := conf.Check("p", fset, []*ast.File{f}, &info) + if err != nil { + t.Fatal(err) + } + + N := pkg.Scope().Lookup("N").Type().(*Named) + + // Find the generic methods stored on N. + gm, gn := N.Method(0), N.Method(1) + if gm.Name() == "n" { + gm, gn = gn, gm + } + + // Collect objects from info. + var dm, dn *Func // the declared methods + var dmm, dmn *Func // the methods used in the body of m + for _, decl := range f.Decls { + fdecl, ok := decl.(*ast.FuncDecl) + if !ok { + continue + } + def := info.Defs[fdecl.Name].(*Func) + switch fdecl.Name.Name { + case "m": + dm = def + ast.Inspect(fdecl.Body, func(n ast.Node) bool { + if call, ok := n.(*ast.CallExpr); ok { + sel := call.Fun.(*ast.SelectorExpr) + use := info.Uses[sel.Sel].(*Func) + selection := info.Selections[sel] + if selection.Kind() != MethodVal { + t.Errorf("Selection kind = %v, want %v", selection.Kind(), MethodVal) + } + if selection.Obj() != use { + t.Errorf("info.Selections contains %v, want %v", selection.Obj(), use) + } + switch sel.Sel.Name { + case "m": + dmm = use + case "n": + dmn = use + } + } + return true + }) + case "n": + dn = def + } + } + + if gm != dm { + t.Errorf(`N.Method(...) returns %v for "m", but Info.Defs has %v`, gm, dm) + } + if gn != dn { + t.Errorf(`N.Method(...) returns %v for "m", but Info.Defs has %v`, gm, dm) + } + if dmm != dm { + t.Errorf(`Inside "m", r.m uses %v, want the defined func %v`, dmm, dm) + } + if dmn == dn { + t.Errorf(`Inside "m", r.n uses %v, want a func distinct from %v`, dmm, dm) + } +} + func TestImplicitsInfo(t *testing.T) { testenv.MustHaveGoBuild(t) @@ -520,6 +836,17 @@ func TestImplicitsInfo(t *testing.T) { {`package p8; func f(int) {}`, "field: var int"}, {`package p9; func f() (complex64) { return 0 }`, "field: var complex64"}, {`package p10; type T struct{}; func (*T) f() {}`, "field: var *p10.T"}, + + // Tests using generics. + {`package generic_f0; func f[T any](x int) {}`, ""}, // no Implicits entry + {`package generic_f1; func f[T any](int) {}`, "field: var int"}, + {`package generic_f2; func f[T any](T) {}`, "field: var T"}, + {`package generic_f3; func f[T any]() (complex64) { return 0 }`, "field: var complex64"}, + {`package generic_f4; func f[T any](t T) (T) { return t }`, "field: var T"}, + {`package generic_t0; type T[A any] struct{}; func (*T[_]) f() {}`, "field: var *generic_t0.T[_]"}, + {`package generic_t1; type T[A any] struct{}; func _(x interface{}) { switch t := x.(type) { case T[int]: _ = t } }`, "caseClause: var t generic_t1.T[int]"}, + {`package generic_t2; type T[A any] struct{}; func _[P any](x interface{}) { switch t := x.(type) { case T[P]: _ = t } }`, "caseClause: var t generic_t2.T[P]"}, + {`package generic_t3; func _[P any](x interface{}) { switch t := x.(type) { case P: _ = t } }`, "caseClause: var t P"}, } for _, test := range tests { @@ -1229,6 +1556,18 @@ var _ = a.C2 makePkg("main", mainSrc) // don't crash when type-checking this package } +func TestLookupFieldOrMethodOnNil(t *testing.T) { + // LookupFieldOrMethod on a nil type is expected to produce a run-time panic. + defer func() { + const want = "LookupFieldOrMethod on nil type" + p := recover() + if s, ok := p.(string); !ok || s != want { + t.Fatalf("got %v, want %s", p, want) + } + }() + LookupFieldOrMethod(nil, false, nil, "") +} + func TestLookupFieldOrMethod(t *testing.T) { t.Skip("skipping for gccgo--no importer") // Test cases assume a lookup of the form a.f or x.f, where a stands for an @@ -1435,6 +1774,13 @@ func F(){ } } +// newDefined creates a new defined type named T with the given underlying type. +// Helper function for use with TestIncompleteInterfaces only. +func newDefined(underlying Type) *Named { + tname := NewTypeName(token.NoPos, nil, "T", nil) + return NewNamed(tname, underlying, nil) +} + func TestConvertibleTo(t *testing.T) { for _, test := range []struct { v, t Type @@ -1442,6 +1788,7 @@ func TestConvertibleTo(t *testing.T) { }{ {Typ[Int], Typ[Int], true}, {Typ[Int], Typ[Float32], true}, + {Typ[Int], Typ[String], true}, {newDefined(Typ[Int]), Typ[Int], true}, {newDefined(new(Struct)), new(Struct), true}, {newDefined(Typ[Int]), new(Struct), false}, @@ -1449,8 +1796,7 @@ func TestConvertibleTo(t *testing.T) { {NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Int], 10)), true}, {NewSlice(Typ[Int]), NewArray(Typ[Int], 10), false}, {NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Uint], 10)), false}, - // Untyped string values are not permitted by the spec, so the below - // behavior is undefined. + // Untyped string values are not permitted by the spec, so the behavior below is undefined. {Typ[UntypedString], Typ[String], true}, } { if got := ConvertibleTo(test.v, test.t); got != test.want { @@ -1482,6 +1828,56 @@ func TestAssignableTo(t *testing.T) { } } +func TestIdentical(t *testing.T) { + // For each test, we compare the types of objects X and Y in the source. + tests := []struct { + src string + want bool + }{ + // Basic types. + {"var X int; var Y int", true}, + {"var X int; var Y string", false}, + + // TODO: add more tests for complex types. + + // Named types. + {"type X int; type Y int", false}, + + // Aliases. + {"type X = int; type Y = int", true}, + + // Functions. + {`func X(int) string { return "" }; func Y(int) string { return "" }`, true}, + {`func X() string { return "" }; func Y(int) string { return "" }`, false}, + {`func X(int) string { return "" }; func Y(int) {}`, false}, + + // Generic functions. Type parameters should be considered identical modulo + // renaming. See also issue #49722. + {`func X[P ~int](){}; func Y[Q ~int]() {}`, true}, + {`func X[P1 any, P2 ~*P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, true}, + {`func X[P1 any, P2 ~[]P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, false}, + {`func X[P ~int](P){}; func Y[Q ~int](Q) {}`, true}, + {`func X[P ~string](P){}; func Y[Q ~int](Q) {}`, false}, + {`func X[P ~int]([]P){}; func Y[Q ~int]([]Q) {}`, true}, + } + + for _, test := range tests { + pkg, err := pkgForMode("test", "package p;"+test.src, nil, 0) + if err != nil { + t.Errorf("%s: incorrect test case: %s", test.src, err) + continue + } + X := pkg.Scope().Lookup("X") + Y := pkg.Scope().Lookup("Y") + if X == nil || Y == nil { + t.Fatal("test must declare both X and Y") + } + if got := Identical(X.Type(), Y.Type()); got != test.want { + t.Errorf("Identical(%s, %s) = %t, want %t", X.Type(), Y.Type(), got, test.want) + } + } +} + func TestIdentical_issue15173(t *testing.T) { // Identical should allow nil arguments and be symmetric. for _, test := range []struct { @@ -1499,6 +1895,48 @@ func TestIdentical_issue15173(t *testing.T) { } } +func TestIdenticalUnions(t *testing.T) { + tname := NewTypeName(token.NoPos, nil, "myInt", nil) + myInt := NewNamed(tname, Typ[Int], nil) + tmap := map[string]*Term{ + "int": NewTerm(false, Typ[Int]), + "~int": NewTerm(true, Typ[Int]), + "string": NewTerm(false, Typ[String]), + "~string": NewTerm(true, Typ[String]), + "myInt": NewTerm(false, myInt), + } + makeUnion := func(s string) *Union { + parts := strings.Split(s, "|") + var terms []*Term + for _, p := range parts { + term := tmap[p] + if term == nil { + t.Fatalf("missing term %q", p) + } + terms = append(terms, term) + } + return NewUnion(terms) + } + for _, test := range []struct { + x, y string + want bool + }{ + // These tests are just sanity checks. The tests for type sets and + // interfaces provide much more test coverage. + {"int|~int", "~int", true}, + {"myInt|~int", "~int", true}, + {"int|string", "string|int", true}, + {"int|int|string", "string|int", true}, + {"myInt|string", "int|string", false}, + } { + x := makeUnion(test.x) + y := makeUnion(test.y) + if got := Identical(x, y); got != test.want { + t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got) + } + } +} + func TestIssue15305(t *testing.T) { const src = "package p; func f() int16; var _ = f(undef)" fset := token.NewFileSet() @@ -1718,3 +2156,211 @@ func f(x T) T { return foo.F(x) } } } } + +func TestInstantiate(t *testing.T) { + // eventually we like more tests but this is a start + const src = "package p; type T[P any] *T[P]" + pkg, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + + // type T should have one type parameter + T := pkg.Scope().Lookup("T").Type().(*Named) + if n := T.TypeParams().Len(); n != 1 { + t.Fatalf("expected 1 type parameter; found %d", n) + } + + // instantiation should succeed (no endless recursion) + // even with a nil *Checker + res, err := Instantiate(nil, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + + // instantiated type should point to itself + if p := res.Underlying().(*Pointer).Elem(); p != res { + t.Fatalf("unexpected result type: %s points to %s", res, p) + } +} + +func TestInstantiateErrors(t *testing.T) { + tests := []struct { + src string // by convention, T must be the type being instantiated + targs []Type + wantAt int // -1 indicates no error + }{ + {"type T[P interface{~string}] int", []Type{Typ[Int]}, 0}, + {"type T[P1 interface{int}, P2 interface{~string}] int", []Type{Typ[Int], Typ[Int]}, 1}, + {"type T[P1 any, P2 interface{~[]P1}] int", []Type{Typ[Int], NewSlice(Typ[String])}, 1}, + {"type T[P1 interface{~[]P2}, P2 any] int", []Type{NewSlice(Typ[String]), Typ[Int]}, 0}, + } + + for _, test := range tests { + src := "package p; " + test.src + pkg, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + + T := pkg.Scope().Lookup("T").Type().(*Named) + + _, err = Instantiate(nil, T, test.targs, true) + if err == nil { + t.Fatalf("Instantiate(%v, %v) returned nil error, want non-nil", T, test.targs) + } + + var argErr *ArgumentError + if !errors.As(err, &argErr) { + t.Fatalf("Instantiate(%v, %v): error is not an *ArgumentError", T, test.targs) + } + + if argErr.Index != test.wantAt { + t.Errorf("Instantate(%v, %v): error at index %d, want index %d", T, test.targs, argErr.Index, test.wantAt) + } + } +} + +func TestArgumentErrorUnwrapping(t *testing.T) { + var err error = &ArgumentError{ + Index: 1, + Err: Error{Msg: "test"}, + } + var e Error + if !errors.As(err, &e) { + t.Fatalf("error %v does not wrap types.Error", err) + } + if e.Msg != "test" { + t.Errorf("e.Msg = %q, want %q", e.Msg, "test") + } +} + +func TestInstanceIdentity(t *testing.T) { + imports := make(testImporter) + conf := Config{Importer: imports} + makePkg := func(src string) { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", src, 0) + if err != nil { + t.Fatal(err) + } + name := f.Name.Name + pkg, err := conf.Check(name, fset, []*ast.File{f}, nil) + if err != nil { + t.Fatal(err) + } + imports[name] = pkg + } + makePkg(genericPkg + `lib; type T[P any] struct{}`) + makePkg(genericPkg + `a; import "generic_lib"; var A generic_lib.T[int]`) + makePkg(genericPkg + `b; import "generic_lib"; var B generic_lib.T[int]`) + a := imports["generic_a"].Scope().Lookup("A") + b := imports["generic_b"].Scope().Lookup("B") + if !Identical(a.Type(), b.Type()) { + t.Errorf("mismatching types: a.A: %s, b.B: %s", a.Type(), b.Type()) + } +} + +func TestImplements(t *testing.T) { + const src = ` +package p + +type EmptyIface interface{} + +type I interface { + m() +} + +type C interface { + m() + ~int +} + +type Integer interface{ + int8 | int16 | int32 | int64 +} + +type EmptyTypeSet interface{ + Integer + ~string +} + +type N1 int +func (N1) m() {} + +type N2 int +func (*N2) m() {} + +type N3 int +func (N3) m(int) {} + +type N4 string +func (N4) m() + +type Bad Bad // invalid type +` + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "p.go", src, 0) + if err != nil { + t.Fatal(err) + } + conf := Config{Error: func(error) {}} + pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) + + scope := pkg.Scope() + var ( + EmptyIface = scope.Lookup("EmptyIface").Type().Underlying().(*Interface) + I = scope.Lookup("I").Type().(*Named) + II = I.Underlying().(*Interface) + C = scope.Lookup("C").Type().(*Named) + CI = C.Underlying().(*Interface) + Integer = scope.Lookup("Integer").Type().Underlying().(*Interface) + EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface) + N1 = scope.Lookup("N1").Type() + N1p = NewPointer(N1) + N2 = scope.Lookup("N2").Type() + N2p = NewPointer(N2) + N3 = scope.Lookup("N3").Type() + N4 = scope.Lookup("N4").Type() + Bad = scope.Lookup("Bad").Type() + ) + + tests := []struct { + t Type + i *Interface + want bool + }{ + {I, II, true}, + {I, CI, false}, + {C, II, true}, + {C, CI, true}, + {Typ[Int8], Integer, true}, + {Typ[Int64], Integer, true}, + {Typ[String], Integer, false}, + {EmptyTypeSet, II, true}, + {EmptyTypeSet, EmptyTypeSet, true}, + {Typ[Int], EmptyTypeSet, false}, + {N1, II, true}, + {N1, CI, true}, + {N1p, II, true}, + {N1p, CI, false}, + {N2, II, false}, + {N2, CI, false}, + {N2p, II, true}, + {N2p, CI, false}, + {N3, II, false}, + {N3, CI, false}, + {N4, II, true}, + {N4, CI, false}, + {Bad, II, false}, + {Bad, CI, false}, + {Bad, EmptyIface, true}, + } + + for _, test := range tests { + if got := Implements(test.t, test.i); got != test.want { + t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want) + } + } +} diff --git a/libgo/go/go/types/api_typeparams.go b/libgo/go/go/types/api_typeparams.go deleted file mode 100644 index ed744c4..0000000 --- a/libgo/go/go/types/api_typeparams.go +++ /dev/null @@ -1,53 +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. - -//go:build typeparams -// +build typeparams - -package types - -import ( - "go/ast" -) - -type ( - Inferred = _Inferred - Sum = _Sum - TypeParam = _TypeParam -) - -func NewSum(types []Type) Type { return _NewSum(types) } - -func (s *Signature) TParams() []*TypeName { return s._TParams() } -func (s *Signature) SetTParams(tparams []*TypeName) { s._SetTParams(tparams) } - -func (t *Interface) HasTypeList() bool { return t._HasTypeList() } -func (t *Interface) IsComparable() bool { return t._IsComparable() } -func (t *Interface) IsConstraint() bool { return t._IsConstraint() } - -func (t *Named) TParams() []*TypeName { return t._TParams() } -func (t *Named) TArgs() []Type { return t._TArgs() } -func (t *Named) SetTArgs(args []Type) { t._SetTArgs(args) } - -// Info is documented in api_notypeparams.go. -type Info struct { - Types map[ast.Expr]TypeAndValue - - // Inferred maps calls of parameterized functions that use type inference to - // the Inferred type arguments and signature of the function called. The - // recorded "call" expression may be an *ast.CallExpr (as in f(x)), or an - // *ast.IndexExpr (s in f[T]). - Inferred map[ast.Expr]_Inferred - - Defs map[*ast.Ident]Object - Uses map[*ast.Ident]Object - Implicits map[ast.Node]Object - Selections map[*ast.SelectorExpr]*Selection - Scopes map[ast.Node]*Scope - InitOrder []*Initializer -} - -func getInferred(info *Info) map[ast.Expr]_Inferred { - return info.Inferred -} diff --git a/libgo/go/go/types/api_typeparams_test.go b/libgo/go/go/types/api_typeparams_test.go deleted file mode 100644 index 15c9bf0..0000000 --- a/libgo/go/go/types/api_typeparams_test.go +++ /dev/null @@ -1,139 +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. - -//go:build typeparams -// +build typeparams - -package types_test - -import ( - "fmt" - "go/ast" - "testing" - - . "go/types" -) - -func TestInferredInfo(t *testing.T) { - var tests = []struct { - src string - fun string - targs []string - sig string - }{ - {genericPkg + `p0; func f[T any](T); func _() { f(42) }`, - `f`, - []string{`int`}, - `func(int)`, - }, - {genericPkg + `p1; func f[T any](T) T; func _() { f('@') }`, - `f`, - []string{`rune`}, - `func(rune) rune`, - }, - {genericPkg + `p2; func f[T any](...T) T; func _() { f(0i) }`, - `f`, - []string{`complex128`}, - `func(...complex128) complex128`, - }, - {genericPkg + `p3; func f[A, B, C any](A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`, - `f`, - []string{`float64`, `string`, `byte`}, - `func(float64, *string, []byte)`, - }, - {genericPkg + `p4; func f[A, B any](A, *B, ...[]B); func _() { f(1.2, new(byte)) }`, - `f`, - []string{`float64`, `byte`}, - `func(float64, *byte, ...[]byte)`, - }, - - {genericPkg + `s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`, - `f`, - []string{`string`, `*string`}, - `func(x string)`, - }, - {genericPkg + `s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`, - `f`, - []string{`int`, `*int`}, - `func(x []int)`, - }, - {genericPkg + `s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`, - `f`, - []string{`int`, `chan<- int`}, - `func(x []int)`, - }, - {genericPkg + `s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`, - `f`, - []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, - `func(x []int)`, - }, - - {genericPkg + `t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`, - `f`, - []string{`string`, `*string`}, - `func() string`, - }, - {genericPkg + `t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`, - `f`, - []string{`int`, `chan<- int`}, - `func() []int`, - }, - {genericPkg + `t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`, - `f`, - []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, - `func() []int`, - }, - } - - for _, test := range tests { - info := Info{} - info.Inferred = make(map[ast.Expr]Inferred) - name, err := mayTypecheck(t, "InferredInfo", test.src, &info) - if err != nil { - t.Errorf("package %s: %v", name, err) - continue - } - - // look for inferred type arguments and signature - var targs []Type - var sig *Signature - for call, inf := range info.Inferred { - var fun ast.Expr - switch x := call.(type) { - case *ast.CallExpr: - fun = x.Fun - case *ast.IndexExpr: - fun = x.X - default: - panic(fmt.Sprintf("unexpected call expression type %T", call)) - } - if ExprString(fun) == test.fun { - targs = inf.Targs - sig = inf.Sig - break - } - } - if targs == nil { - t.Errorf("package %s: no inferred information found for %s", name, test.fun) - continue - } - - // check that type arguments are correct - if len(targs) != len(test.targs) { - t.Errorf("package %s: got %d type arguments; want %d", name, len(targs), len(test.targs)) - continue - } - for i, targ := range targs { - if got := targ.String(); got != test.targs[i] { - t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i]) - continue - } - } - - // check that signature is correct - if got := sig.String(); got != test.sig { - t.Errorf("package %s: got %s; want %s", name, got, test.sig) - } - } -} diff --git a/libgo/go/go/types/array.go b/libgo/go/go/types/array.go new file mode 100644 index 0000000..5b28474 --- /dev/null +++ b/libgo/go/go/types/array.go @@ -0,0 +1,25 @@ +// Copyright 2011 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 types + +// An Array represents an array type. +type Array struct { + len int64 + elem Type +} + +// NewArray returns a new array type for the given element type and length. +// A negative length indicates an unknown length. +func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} } + +// Len returns the length of array a. +// A negative result indicates an unknown length. +func (a *Array) Len() int64 { return a.len } + +// Elem returns element type of array a. +func (a *Array) Elem() Type { return a.elem } + +func (t *Array) Underlying() Type { return t } +func (t *Array) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/assignments.go b/libgo/go/go/types/assignments.go index 18eae62..f75b8b6 100644 --- a/libgo/go/go/types/assignments.go +++ b/libgo/go/go/types/assignments.go @@ -7,8 +7,9 @@ package types import ( + "fmt" "go/ast" - "go/token" + "strings" ) // assignment reports whether x can be assigned to a variable of type T, @@ -37,7 +38,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) { // bool, rune, int, float64, complex128 or string respectively, depending // on whether the value is a boolean, rune, integer, floating-point, // complex, or string constant." - if T == nil || IsInterface(T) { + if T == nil || IsInterface(T) && !isTypeParam(T) { if T == nil && x.typ == Typ[UntypedNil] { check.errorf(x, _UntypedNil, "use of untyped nil in %s", context) x.mode = invalid @@ -71,8 +72,8 @@ func (check *Checker) assignment(x *operand, T Type, context string) { } // A generic (non-instantiated) function value cannot be assigned to a variable. - if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { - check.errorf(x, _Todo, "cannot use generic function %s without instantiation in %s", x, context) + if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 { + check.errorf(x, _WrongTypeArgCount, "cannot use generic function %s without instantiation in %s", x, context) } // spec: "If a left-hand side is the blank identifier, any typed or @@ -84,10 +85,18 @@ func (check *Checker) assignment(x *operand, T Type, context string) { reason := "" if ok, code := x.assignableTo(check, T, &reason); !ok { - if reason != "" { - check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason) + if compilerErrorMessages { + if reason != "" { + check.errorf(x, code, "cannot use %s as type %s in %s:\n\t%s", x, T, context, reason) + } else { + check.errorf(x, code, "cannot use %s as type %s in %s", x, T, context) + } } else { - check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context) + if reason != "" { + check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason) + } else { + check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context) + } } x.mode = invalid } @@ -229,14 +238,89 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { return x.typ } -// If returnPos is valid, initVars is called to type-check the assignment of -// return expressions, and returnPos is the position of the return statement. -func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.Pos) { - rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && !returnPos.IsValid()) +// operandTypes returns the list of types for the given operands. +func operandTypes(list []*operand) (res []Type) { + for _, x := range list { + res = append(res, x.typ) + } + return res +} + +// varTypes returns the list of types for the given variables. +func varTypes(list []*Var) (res []Type) { + for _, x := range list { + res = append(res, x.typ) + } + return res +} + +// typesSummary returns a string of the form "(t1, t2, ...)" where the +// ti's are user-friendly string representations for the given types. +// If variadic is set and the last type is a slice, its string is of +// the form "...E" where E is the slice's element type. +func (check *Checker) typesSummary(list []Type, variadic bool) string { + var res []string + for i, t := range list { + var s string + switch { + case t == nil: + fallthrough // should not happen but be cautious + case t == Typ[Invalid]: + s = "<T>" + case isUntyped(t): + if isNumeric(t) { + // Do not imply a specific type requirement: + // "have number, want float64" is better than + // "have untyped int, want float64" or + // "have int, want float64". + s = "number" + } else { + // If we don't have a number, omit the "untyped" qualifier + // for compactness. + s = strings.Replace(t.(*Basic).name, "untyped ", "", -1) + } + case variadic && i == len(list)-1: + s = check.sprintf("...%s", t.(*Slice).elem) + } + if s == "" { + s = check.sprintf("%s", t) + } + res = append(res, s) + } + return "(" + strings.Join(res, ", ") + ")" +} + +func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) { + measure := func(x int, unit string) string { + s := fmt.Sprintf("%d %s", x, unit) + if x != 1 { + s += "s" + } + return s + } + + vars := measure(nvars, "variable") + vals := measure(nvals, "value") + rhs0 := rhs[0] + + if len(rhs) == 1 { + if call, _ := unparen(rhs0).(*ast.CallExpr); call != nil { + check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals) + return + } + } + check.errorf(rhs0, _WrongAssignCount, "assignment mismatch: %s but %s", vars, vals) +} + +// If returnStmt != nil, initVars is called to type-check the assignment +// of return expressions, and returnStmt is the return statement. +func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnStmt ast.Stmt) { + rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && returnStmt == nil) if len(lhs) != len(rhs) { // invalidate lhs for _, obj := range lhs { + obj.used = true // avoid declared but not used errors if obj.typ == nil { obj.typ = Typ[Invalid] } @@ -247,16 +331,32 @@ func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.P return } } - if returnPos.IsValid() { - check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs)) + if returnStmt != nil { + var at positioner = returnStmt + qualifier := "not enough" + if len(rhs) > len(lhs) { + at = rhs[len(lhs)].expr // report at first extra value + qualifier = "too many" + } else if len(rhs) > 0 { + at = rhs[len(rhs)-1].expr // report at last value + } + check.errorf(at, _WrongResultCount, "%s return values\n\thave %s\n\twant %s", + qualifier, + check.typesSummary(operandTypes(rhs), false), + check.typesSummary(varTypes(lhs), false), + ) return } - check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs)) + if compilerErrorMessages { + check.assignError(origRHS, len(lhs), len(rhs)) + } else { + check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs)) + } return } context := "assignment" - if returnPos.IsValid() { + if returnStmt != nil { context = "return statement" } @@ -285,7 +385,11 @@ func (check *Checker) assignVars(lhs, origRHS []ast.Expr) { return } } - check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs)) + if compilerErrorMessages { + check.assignError(origRHS, len(lhs), len(rhs)) + } else { + check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs)) + } return } @@ -364,7 +468,7 @@ func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) { } } - check.initVars(lhsVars, rhs, token.NoPos) + check.initVars(lhsVars, rhs, nil) // process function literals in rhs expressions before scope changes check.processDelayed(top) diff --git a/libgo/go/go/types/basic.go b/libgo/go/go/types/basic.go new file mode 100644 index 0000000..215923f --- /dev/null +++ b/libgo/go/go/types/basic.go @@ -0,0 +1,82 @@ +// Copyright 2011 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 types + +// BasicKind describes the kind of basic type. +type BasicKind int + +const ( + Invalid BasicKind = iota // type is invalid + + // predeclared types + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + String + UnsafePointer + + // types for untyped values + UntypedBool + UntypedInt + UntypedRune + UntypedFloat + UntypedComplex + UntypedString + UntypedNil + + // aliases + Byte = Uint8 + Rune = Int32 +) + +// BasicInfo is a set of flags describing properties of a basic type. +type BasicInfo int + +// Properties of basic types. +const ( + IsBoolean BasicInfo = 1 << iota + IsInteger + IsUnsigned + IsFloat + IsComplex + IsString + IsUntyped + + IsOrdered = IsInteger | IsFloat | IsString + IsNumeric = IsInteger | IsFloat | IsComplex + IsConstType = IsBoolean | IsNumeric | IsString +) + +// A Basic represents a basic type. +type Basic struct { + kind BasicKind + info BasicInfo + name string +} + +// Kind returns the kind of basic type b. +func (b *Basic) Kind() BasicKind { return b.kind } + +// Info returns information about properties of basic type b. +func (b *Basic) Info() BasicInfo { return b.info } + +// Name returns the name of basic type b. +func (b *Basic) Name() string { return b.name } + +func (t *Basic) Underlying() Type { return t } +func (t *Basic) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/builtins.go b/libgo/go/go/types/builtins.go index 2a2d54d..35a2d1a 100644 --- a/libgo/go/go/types/builtins.go +++ b/libgo/go/go/types/builtins.go @@ -47,7 +47,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b default: // make argument getter xlist, _ := check.exprList(call.Args, false) - arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) } + arg = func(x *operand, i int) { *x = *xlist[i] } nargs = len(xlist) // evaluate first argument, if present if nargs > 0 { @@ -83,7 +83,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // of S and the respective parameter passing rules apply." S := x.typ var T Type - if s := asSlice(S); s != nil { + if s, _ := structuralType(S).(*Slice); s != nil { T = s.elem } else { check.invalidArg(x, _InvalidAppend, "%s is not a slice", x) @@ -102,7 +102,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b if x.mode == invalid { return } - if isString(x.typ) { + if t := structuralString(x.typ); t != nil && isString(t) { if check.Types != nil { sig := makeSig(S, S, x.typ) sig.variadic = true @@ -130,7 +130,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b arg(&x, i) xlist = append(xlist, &x) } - check.arguments(call, sig, nil, xlist) // discard result (we know the result type) + check.arguments(call, sig, nil, xlist, nil) // discard result (we know the result type) // ok to continue even if check.arguments reported errors x.mode = value @@ -145,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b mode := invalid var typ Type var val constant.Value - switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) { + switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant_ { @@ -179,9 +179,12 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b mode = value } - case *_Sum: - if t.is(func(t Type) bool { - switch t := under(t).(type) { + case *Interface: + if !isTypeParam(x.typ) { + break + } + if t.typeSet().underIs(func(t Type) bool { + switch t := arrayPtrDeref(t).(type) { case *Basic: if isString(t) && id == _Len { return true @@ -217,19 +220,23 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Close: // close(c) - c := asChan(x.typ) - if c == nil { - check.invalidArg(x, _InvalidClose, "%s is not a channel", x) - return - } - if c.dir == RecvOnly { - check.invalidArg(x, _InvalidClose, "%s must not be a receive-only channel", x) + if !underIs(x.typ, func(u Type) bool { + uch, _ := u.(*Chan) + if uch == nil { + check.invalidOp(x, _InvalidClose, "cannot close non-channel %s", x) + return false + } + if uch.dir == RecvOnly { + check.invalidOp(x, _InvalidClose, "cannot close receive-only channel %s", x) + return false + } + return true + }) { return } - x.mode = novalue if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, c)) + check.recordBuiltinType(call.Fun, makeSig(nil, x.typ)) } case _Complex: @@ -286,14 +293,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } // both argument types must be identical - if !check.identical(x.typ, y.typ) { + if !Identical(x.typ, y.typ) { check.invalidArg(x, _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ) return } // the argument types must be of floating-point type - f := func(x Type) Type { - if t := asBasic(x); t != nil { + // (applyTypeFunc never calls f with a type parameter) + f := func(typ Type) Type { + assert(!isTypeParam(typ)) + if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Float32: return Typ[Complex64] @@ -326,33 +335,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Copy: // copy(x, y []T) int - var dst Type - if t := asSlice(x.typ); t != nil { - dst = t.elem - } + dst, _ := structuralType(x.typ).(*Slice) var y operand arg(&y, 1) if y.mode == invalid { return } - var src Type - switch t := optype(y.typ).(type) { - case *Basic: - if isString(y.typ) { - src = universeByte - } - case *Slice: - src = t.elem + src0 := structuralString(y.typ) + if src0 != nil && isString(src0) { + src0 = NewSlice(universeByte) } + src, _ := src0.(*Slice) if dst == nil || src == nil { check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y) return } - if !check.identical(dst, src) { - check.invalidArg(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) + if !Identical(dst.elem, src.elem) { + check.errorf(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst.elem, src.elem) return } @@ -363,25 +365,40 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b x.typ = Typ[Int] case _Delete: - // delete(m, k) - m := asMap(x.typ) - if m == nil { - check.invalidArg(x, _InvalidDelete, "%s is not a map", x) + // delete(map_, key) + // map_ must be a map type or a type parameter describing map types. + // The key cannot be a type parameter for now. + map_ := x.typ + var key Type + if !underIs(map_, func(u Type) bool { + map_, _ := u.(*Map) + if map_ == nil { + check.invalidArg(x, _InvalidDelete, "%s is not a map", x) + return false + } + if key != nil && !Identical(map_.key, key) { + check.invalidArg(x, _InvalidDelete, "maps of %s must have identical key types", x) + return false + } + key = map_.key + return true + }) { return } + arg(x, 1) // k if x.mode == invalid { return } - check.assignment(x, m.key, "argument to delete") + check.assignment(x, key, "argument to delete") if x.mode == invalid { return } x.mode = novalue if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) + check.recordBuiltinType(call.Fun, makeSig(nil, map_, key)) } case _Imag, _Real: @@ -410,8 +427,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } // the argument must be of complex type - f := func(x Type) Type { - if t := asBasic(x); t != nil { + // (applyTypeFunc never calls f with a type parameter) + f := func(typ Type) Type { + assert(!isTypeParam(typ)) + if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Complex64: return Typ[Float32] @@ -460,39 +479,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return } - min, max := -1, 10 - var valid func(t Type) bool - valid = func(t Type) bool { - var m int - switch t := optype(t).(type) { - case *Slice: - m = 2 - case *Map, *Chan: - m = 1 - case *_Sum: - return t.is(valid) - default: - return false - } - if m > min { - min = m - } - if m+1 < max { - max = m + 1 - } - return true - } - - if !valid(T) { + var min int // minimum number of arguments + switch structuralType(T).(type) { + case *Slice: + min = 2 + case *Map, *Chan: + min = 1 + case nil: + check.errorf(arg0, _InvalidMake, "cannot make %s: no structural type", arg0) + return + default: check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0) return } - if nargs < min || max < nargs { - if min == max { - check.errorf(call, _WrongArgCount, "%v expects %d arguments; found %d", call, min, nargs) - } else { - check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, max, nargs) - } + if nargs < min || min+1 < nargs { + check.invalidOp(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs) return } @@ -612,19 +613,22 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Alignof: // unsafe.Alignof(x T) uintptr - if asTypeParam(x.typ) != nil { - check.invalidOp(call, _Todo, "unsafe.Alignof undefined for %s", x) - return - } check.assignment(x, nil, "argument to unsafe.Alignof") if x.mode == invalid { return } - x.mode = constant_ - x.val = constant.MakeInt64(check.conf.alignof(x.typ)) + if hasVarSize(x.typ) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ)) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.alignof(x.typ)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector @@ -644,7 +648,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b base := derefStructPtr(x.typ) sel := selx.Sel.Name - obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel) + obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) switch obj.(type) { case nil: check.invalidArg(x, _MissingFieldOrMethod, "%s has no single field %s", base, sel) @@ -662,30 +666,52 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return } - // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? + // TODO(gri) Should we pass x.typ instead of base (and have indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) - offs := check.conf.offsetof(base, index) - x.mode = constant_ - x.val = constant.MakeInt64(offs) + // record the selector expression (was bug - issue #47895) + { + mode := value + if x.mode == variable || indirect { + mode = variable + } + check.record(&operand{mode, selx, obj.Type(), nil, 0}) + } + + // The field offset is considered a variable even if the field is declared before + // the part of the struct which is variable-sized. This makes both the rules + // simpler and also permits (or at least doesn't prevent) a compiler from re- + // arranging struct fields if it wanted to. + if hasVarSize(base) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type())) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.offsetof(base, index)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr - if asTypeParam(x.typ) != nil { - check.invalidOp(call, _Todo, "unsafe.Sizeof undefined for %s", x) - return - } check.assignment(x, nil, "argument to unsafe.Sizeof") if x.mode == invalid { return } - x.mode = constant_ - x.val = constant.MakeInt64(check.conf.sizeof(x.typ)) + if hasVarSize(x.typ) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ)) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.sizeof(x.typ)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Slice: // unsafe.Slice(ptr *T, len IntegerType) []T @@ -694,7 +720,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return } - typ := asPointer(x.typ) + typ, _ := under(x.typ).(*Pointer) if typ == nil { check.invalidArg(x, _InvalidUnsafeSlice, "%s is not a pointer", x) return @@ -744,7 +770,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b var t operand x1 := x for _, arg := range call.Args { - check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) + check.rawExpr(x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T)) check.dump("%v: %s", x1.Pos(), x1) x1 = &t // use incoming x only for first argument } @@ -757,6 +783,25 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return true } +// hasVarSize reports if the size of type t is variable due to type parameters. +func hasVarSize(t Type) bool { + switch u := under(t).(type) { + case *Array: + return hasVarSize(u.elem) + case *Struct: + for _, f := range u.fields { + if hasVarSize(f.typ) { + return true + } + } + case *Interface: + return isTypeParam(t) + case *Named, *Union: + unreachable() + } + return false +} + // applyTypeFunc applies f to x. If x is a type parameter, // the result is a type parameter constrained by an new // interface bound. The type bounds for that interface @@ -765,13 +810,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // applyTypeFunc returns nil. // If x is not a type parameter, the result is f(x). func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { - if tp := asTypeParam(x); tp != nil { + if tp, _ := x.(*TypeParam); tp != nil { // Test if t satisfies the requirements for the argument // type and collect possible result types at the same time. - var rtypes []Type - if !tp.Bound().is(func(x Type) bool { - if r := f(x); r != nil { - rtypes = append(rtypes, r) + var terms []*Term + if !tp.is(func(t *term) bool { + if t == nil { + return false + } + if r := f(t.typ); r != nil { + terms = append(terms, NewTerm(t.tilde, r)) return true } return false @@ -779,11 +827,12 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { return nil } - // construct a suitable new type parameter - tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil) - ptyp := check.newTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect - tsum := _NewSum(rtypes) - ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum} + // Construct a suitable new type parameter for the result type. + // The type parameter is placed in the current package so export/import + // works as expected. + tpar := NewTypeName(token.NoPos, check.pkg, "<type parameter>", nil) + ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect + ptyp.index = tp.index return ptyp } @@ -807,12 +856,11 @@ func makeSig(res Type, args ...Type) *Signature { return &Signature{params: params, results: result} } -// implicitArrayDeref returns A if typ is of the form *A and A is an array; +// arrayPtrDeref returns A if typ is of the form *A and A is an array; // otherwise it returns typ. -// -func implicitArrayDeref(typ Type) Type { +func arrayPtrDeref(typ Type) Type { if p, ok := typ.(*Pointer); ok { - if a := asArray(p.base); a != nil { + if a, _ := under(p.base).(*Array); a != nil { return a } } diff --git a/libgo/go/go/types/builtins_test.go b/libgo/go/go/types/builtins_test.go index c4830d9..cfe075c 100644 --- a/libgo/go/go/types/builtins_test.go +++ b/libgo/go/go/types/builtins_test.go @@ -113,12 +113,15 @@ var builtinCalls = []struct { {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant + {"Alignof", `var x P; _ = unsafe.Alignof(x)`, `func(P) uintptr`}, {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant + {"Offsetof", `var x struct{_ int; f P}; _ = unsafe.Offsetof((&x).f)`, `func(P) uintptr`}, {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant + {"Sizeof", `var x P; _ = unsafe.Sizeof(x)`, `func(P) uintptr`}, {"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`}, {"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`}, @@ -151,10 +154,12 @@ 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) { t.Skip("skipping for gccgo--no default importer") - src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) + src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0) f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Errorf("%s: %s", src0, err) diff --git a/libgo/go/go/types/call.go b/libgo/go/go/types/call.go index 336b526..aa87c48 100644 --- a/libgo/go/go/types/call.go +++ b/libgo/go/go/types/call.go @@ -16,76 +16,102 @@ import ( // funcInst type-checks a function instantiation inst and returns the result in x. // The operand x must be the evaluation of inst.X and its type must be a signature. -func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) { - xlist := typeparams.UnpackExpr(inst.Index) - targs := check.typeList(xlist) +func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) { + if !check.allowVersion(check.pkg, 1, 18) { + check.softErrorf(inNode(ix.Orig, ix.Lbrack), _UnsupportedFeature, "function instantiation requires go1.18 or later") + } + + targs := check.typeList(ix.Indices) if targs == nil { x.mode = invalid - x.expr = inst + x.expr = ix.Orig return } - assert(len(targs) == len(xlist)) + assert(len(targs) == len(ix.Indices)) // check number of type arguments (got) vs number of type parameters (want) sig := x.typ.(*Signature) - got, want := len(targs), len(sig.tparams) + got, want := len(targs), sig.TypeParams().Len() if got > want { - check.errorf(xlist[got-1], _Todo, "got %d type arguments but want %d", got, want) + check.errorf(ix.Indices[got-1], _WrongTypeArgCount, "got %d type arguments but want %d", got, want) x.mode = invalid - x.expr = inst + x.expr = ix.Orig return } - // if we don't have enough type arguments, try type inference - inferred := false - if got < want { - targs = check.infer(inst, sig.tparams, targs, nil, nil, true) + targs = check.infer(ix.Orig, sig.TypeParams().list(), targs, nil, nil) if targs == nil { // error was already reported x.mode = invalid - x.expr = inst + x.expr = ix.Orig return } got = len(targs) - inferred = true } assert(got == want) - // determine argument positions (for error reporting) - // TODO(rFindley) use a positioner here? instantiate would need to be - // updated accordingly. - poslist := make([]token.Pos, len(xlist)) - for i, x := range xlist { - poslist[i] = x.Pos() - } - // instantiate function signature - res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) - assert(res.tparams == nil) // signature is not generic anymore - if inferred { - check.recordInferred(inst, targs, res) - } + res := check.instantiateSignature(x.Pos(), sig, targs, ix.Indices) + assert(res.TypeParams().Len() == 0) // signature is not generic anymore + check.recordInstance(ix.Orig, targs, res) x.typ = res x.mode = value - x.expr = inst + x.expr = ix.Orig +} + +func (check *Checker) instantiateSignature(pos token.Pos, typ *Signature, targs []Type, xlist []ast.Expr) (res *Signature) { + assert(check != nil) + assert(len(targs) == typ.TypeParams().Len()) + + if trace { + check.trace(pos, "-- instantiating %s with %s", typ, targs) + check.indent++ + defer func() { + check.indent-- + check.trace(pos, "=> %s (under = %s)", res, res.Underlying()) + }() + } + + inst := check.instance(pos, typ, targs, check.bestContext(nil)).(*Signature) + assert(len(xlist) <= len(targs)) + + // verify instantiation lazily (was issue #50450) + check.later(func() { + tparams := typ.TypeParams().list() + if i, err := check.verify(pos, tparams, targs); err != nil { + // best position for error reporting + pos := pos + if i < len(xlist) { + pos = xlist[i].Pos() + } + check.softErrorf(atPos(pos), _InvalidTypeArg, "%s", err) + } else { + check.mono.recordInstance(check.pkg, pos, tparams, targs, xlist) + } + }) + + return inst } func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { - var inst *ast.IndexExpr - if iexpr, _ := call.Fun.(*ast.IndexExpr); iexpr != nil { - if check.indexExpr(x, iexpr) { + ix := typeparams.UnpackIndexExpr(call.Fun) + if ix != nil { + if check.indexExpr(x, ix) { // Delay function instantiation to argument checking, // where we combine type and value arguments for type // inference. assert(x.mode == value) - inst = iexpr + } else { + ix = nil } - x.expr = iexpr + x.expr = call.Fun check.record(x) + } else { - check.exprOrType(x, call.Fun) + check.exprOrType(x, call.Fun, true) } + // x.typ may be generic switch x.mode { case invalid: @@ -95,6 +121,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { case typexpr: // conversion + check.nonGeneric(x) + if x.mode == invalid { + return conversion + } T := x.typ x.mode = invalid switch n := len(call.Args); n { @@ -107,10 +137,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { check.errorf(call.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T) break } - if t := asInterface(T); t != nil { - check.completeInterface(token.NoPos, t) - if t._IsConstraint() { - check.errorf(call, _Todo, "cannot use interface %s in conversion (contains type list or is comparable)", T) + if t, _ := under(T).(*Interface); t != nil && !isTypeParam(T) { + if !t.IsMethodSet() { + check.errorf(call, _MisplacedConstraintIface, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", T) break } } @@ -124,6 +153,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { return conversion case builtin: + // no need to check for non-genericity here id := x.id if !check.builtin(x, call, id) { x.mode = invalid @@ -137,9 +167,11 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { } // ordinary function/method call + // signature may be generic cgocall := x.mode == cgofunc - sig := asSignature(x.typ) + // a type parameter may be "called" if all types have the same signature + sig, _ := structuralType(x.typ).(*Signature) if sig == nil { check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x) x.mode = invalid @@ -148,9 +180,10 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { } // evaluate type arguments, if any + var xlist []ast.Expr var targs []Type - if inst != nil { - xlist := typeparams.UnpackExpr(inst.Index) + if ix != nil { + xlist = ix.Indices targs = check.typeList(xlist) if targs == nil { check.use(call.Args...) @@ -161,9 +194,9 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { assert(len(targs) == len(xlist)) // check number of type arguments (got) vs number of type parameters (want) - got, want := len(targs), len(sig.tparams) + got, want := len(targs), sig.TypeParams().Len() if got > want { - check.errorf(xlist[want], _Todo, "got %d type arguments but want %d", got, want) + check.errorf(xlist[want], _WrongTypeArgCount, "got %d type arguments but want %d", got, want) check.use(call.Args...) x.mode = invalid x.expr = call @@ -173,7 +206,13 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { // evaluate arguments args, _ := check.exprList(call.Args, false) - sig = check.arguments(call, sig, targs, args) + isGeneric := sig.TypeParams().Len() > 0 + sig = check.arguments(call, sig, targs, args, xlist) + + if isGeneric && sig.TypeParams().Len() == 0 { + // Update the recorded type of call.Fun to its instantiated type. + check.recordTypeAndValue(call.Fun, value, sig, nil) + } // determine result switch sig.results.Len() { @@ -195,7 +234,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind { // if type inference failed, a parametrized result must be invalidated // (operands cannot have a parametrized type) - if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) { + if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) { x.mode = invalid } @@ -245,7 +284,8 @@ func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*op return } -func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type, args []*operand) (rsig *Signature) { +// xlist is the list of type argument expressions supplied in the source code. +func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type, args []*operand, xlist []ast.Expr) (rsig *Signature) { rsig = sig // TODO(gri) try to eliminate this extra verification loop @@ -314,42 +354,65 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type } // check argument count - switch { - case nargs < npars: - check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun) - return - case nargs > npars: - check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument + if nargs != npars { + var at positioner = call + qualifier := "not enough" + if nargs > npars { + at = args[npars].expr // report at first extra argument + qualifier = "too many" + } else { + at = atPos(call.Rparen) // report at closing ) + } + // take care of empty parameter lists represented by nil tuples + var params []*Var + if sig.params != nil { + params = sig.params.vars + } + check.errorf(at, _WrongArgCount, "%s arguments in call to %s\n\thave %s\n\twant %s", + qualifier, call.Fun, + check.typesSummary(operandTypes(args), false), + check.typesSummary(varTypes(params), sig.variadic), + ) return } // infer type arguments and instantiate signature if necessary - if len(sig.tparams) > 0 { - // TODO(gri) provide position information for targs so we can feed - // it to the instantiate call for better error reporting - targs := check.infer(call, sig.tparams, targs, sigParams, args, true) + if sig.TypeParams().Len() > 0 { + if !check.allowVersion(check.pkg, 1, 18) { + switch call.Fun.(type) { + case *ast.IndexExpr, *ast.IndexListExpr: + ix := typeparams.UnpackIndexExpr(call.Fun) + check.softErrorf(inNode(call.Fun, ix.Lbrack), _UnsupportedFeature, "function instantiation requires go1.18 or later") + default: + check.softErrorf(inNode(call, call.Lparen), _UnsupportedFeature, "implicit function instantiation requires go1.18 or later") + } + } + targs := check.infer(call, sig.TypeParams().list(), targs, sigParams, args) if targs == nil { return // error already reported } // compute result signature - rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) - assert(rsig.tparams == nil) // signature is not generic anymore - check.recordInferred(call, targs, rsig) + rsig = check.instantiateSignature(call.Pos(), sig, targs, xlist) + assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore + check.recordInstance(call.Fun, targs, rsig) // Optimization: Only if the parameter list was adjusted do we // need to compute it from the adjusted list; otherwise we can // simply use the result signature's parameter list. if adjusted { - sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple) } else { sigParams = rsig.params } } // check arguments - for i, a := range args { - check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun)) + if len(args) > 0 { + context := check.sprintf("argument to %s", call.Fun) + for i, a := range args { + check.assignment(a, sigParams.vars[i].typ, context) + } } return @@ -463,36 +526,38 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { } } - check.exprOrType(x, e.X) + check.exprOrType(x, e.X, false) if x.mode == invalid { goto Error } - check.instantiatedOperand(x) - - obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) + obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) if obj == nil { - switch { - case index != nil: + // Don't report another error if the underlying type was invalid (issue #49541). + if under(x.typ) == Typ[Invalid] { + goto Error + } + + if index != nil { // TODO(gri) should provide actual type where the conflict happens check.errorf(e.Sel, _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel) - case indirect: + goto Error + } + + if indirect { check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ) - default: - var why string - if tpar := asTypeParam(x.typ); tpar != nil { - // Type parameter bounds don't specify fields, so don't mention "field". - switch obj := tpar.Bound().obj.(type) { - case nil: - why = check.sprintf("type bound for %s has no method %s", x.typ, sel) - case *TypeName: - why = check.sprintf("interface %s has no method %s", obj.name, sel) - } - } else { - why = check.sprintf("type %s has no field or method %s", x.typ, sel) - } + goto Error + } + var why string + if isInterfacePtr(x.typ) { + why = check.interfacePtrError(x.typ) + } else { + why = check.sprintf("type %s has no field or method %s", x.typ, sel) // Check if capitalization of sel matters and provide better error message in that case. + // TODO(gri) This code only looks at the first character but LookupFieldOrMethod should + // have an (internal) mechanism for case-insensitive lookup that we should use + // instead (see types2). if len(sel) > 0 { var changeCase string if r := rune(sel[0]); unicode.IsUpper(r) { @@ -500,67 +565,18 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { } else { changeCase = string(unicode.ToUpper(r)) + sel[1:] } - if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { + if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { why += ", but does have " + changeCase } } - - check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why) } + check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why) goto Error } // methods may not have a fully set up signature yet if m, _ := obj.(*Func); m != nil { check.objDecl(m, nil) - // If m has a parameterized receiver type, infer the type arguments from - // the actual receiver provided and then substitute the type parameters in - // the signature accordingly. - // TODO(gri) factor this code out - sig := m.typ.(*Signature) - if len(sig.rparams) > 0 { - // For inference to work, we must use the receiver type - // matching the receiver in the actual method declaration. - // If the method is embedded, the matching receiver is the - // embedded struct or interface that declared the method. - // Traverse the embedding to find that type (issue #44688). - recv := x.typ - for i := 0; i < len(index)-1; i++ { - // The embedded type is either a struct or a pointer to - // a struct except for the last one (which we don't need). - recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ - } - - // The method may have a pointer receiver, but the actually provided receiver - // may be a (hopefully addressable) non-pointer value, or vice versa. Here we - // only care about inferring receiver type parameters; to make the inference - // work, match up pointer-ness of receiver and argument. - if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) { - if ptrRecv { - recv = NewPointer(recv) - } else { - recv = recv.(*Pointer).base - } - } - // Disable reporting of errors during inference below. If we're unable to infer - // the receiver type arguments here, the receiver must be be otherwise invalid - // and an error has been reported elsewhere. - arg := operand{mode: variable, expr: x.expr, typ: recv} - targs := check.infer(m, sig.rparams, nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) - if targs == nil { - // We may reach here if there were other errors (see issue #40056). - goto Error - } - // Don't modify m. Instead - for now - make a copy of m and use that instead. - // (If we modify m, some tests will fail; possibly because the m is in use.) - // TODO(gri) investigate and provide a correct explanation here - copy := *m - copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs)) - obj = © - } - // TODO(gri) we also need to do substitution for parameterized interface methods - // (this breaks code in testdata/linalg.go2 at the moment) - // 12/20/2019: Is this TODO still correct? } if x.mode == typexpr { @@ -574,17 +590,37 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) + sig := m.typ.(*Signature) + if sig.recv == nil { + check.error(e, _InvalidDeclCycle, "illegal cycle in method declaration") + goto Error + } + // the receiver type becomes the type of the first function // argument of the method expression's function type var params []*Var - sig := m.typ.(*Signature) if sig.params != nil { params = sig.params.vars } + // Be consistent about named/unnamed parameters. This is not needed + // for type-checking, but the newly constructed signature may appear + // in an error message and then have mixed named/unnamed parameters. + // (An alternative would be to not print parameter names in errors, + // but it's useful to see them; this is cheap and method expressions + // are rare.) + name := "" + if len(params) > 0 && params[0].name != "" { + // name needed + name = sig.recv.name + if name == "" { + name = "_" + } + } + params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...) x.mode = value x.typ = &Signature{ tparams: sig.tparams, - params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "_", x.typ)}, params...)...), + params: NewTuple(params...), results: sig.results, variadic: sig.variadic, } @@ -686,7 +722,7 @@ func (check *Checker) use(arg ...ast.Expr) { // The nil check below is necessary since certain AST fields // may legally be nil (e.g., the ast.SliceExpr.High field). if e != nil { - check.rawExpr(&x, e, nil) + check.rawExpr(&x, e, nil, false) } } } @@ -718,17 +754,9 @@ func (check *Checker) useLHS(arg ...ast.Expr) { } } } - check.rawExpr(&x, e, nil) + check.rawExpr(&x, e, nil, false) if v != nil { v.used = v_used // restore v.used } } } - -// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid]. -func (check *Checker) instantiatedOperand(x *operand) { - if x.mode == typexpr && isGeneric(x.typ) { - check.errorf(x, _Todo, "cannot use generic type %s without instantiation", x.typ) - x.typ = Typ[Invalid] - } -} diff --git a/libgo/go/go/types/chan.go b/libgo/go/go/types/chan.go new file mode 100644 index 0000000..1f7b72b --- /dev/null +++ b/libgo/go/go/types/chan.go @@ -0,0 +1,35 @@ +// Copyright 2011 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 types + +// A Chan represents a channel type. +type Chan struct { + dir ChanDir + elem Type +} + +// A ChanDir value indicates a channel direction. +type ChanDir int + +// The direction of a channel is indicated by one of these constants. +const ( + SendRecv ChanDir = iota + SendOnly + RecvOnly +) + +// NewChan returns a new channel type for the given direction and element type. +func NewChan(dir ChanDir, elem Type) *Chan { + return &Chan{dir: dir, elem: elem} +} + +// Dir returns the direction of channel c. +func (c *Chan) Dir() ChanDir { return c.dir } + +// Elem returns the element type of channel c. +func (c *Chan) Elem() Type { return c.elem } + +func (t *Chan) Underlying() Type { return t } +func (t *Chan) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/check.go b/libgo/go/go/types/check.go index a923c3c..a0c3700 100644 --- a/libgo/go/go/types/check.go +++ b/libgo/go/go/types/check.go @@ -18,6 +18,10 @@ import ( const ( debug = false // leave on during development trace = false // turn on for detailed type resolution traces + + // TODO(rfindley): add compiler error message handling from types2, guarded + // behind this flag, so that we can keep the code in sync. + compilerErrorMessages = false // match compiler error messages ) // If forceStrict is set, the type-checker enforces additional @@ -41,22 +45,24 @@ type exprInfo struct { val constant.Value // constant value; or nil (if not a constant) } -// A context represents the context within which an object is type-checked. -type context struct { +// An environment represents the environment within which an object is +// type-checked. +type environment struct { decl *declInfo // package-level declaration whose init expression/function body is checked scope *Scope // top-most scope for lookups pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) iota constant.Value // value of iota in a constant declaration; nil otherwise errpos positioner // if set, identifier position of a constant with inherited initializer + inTParamList bool // set if inside a type parameter list sig *Signature // function signature if inside a function; nil otherwise isPanic map[*ast.CallExpr]bool // set of panic call expressions (used for termination check) hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions hasCallOrRecv bool // set if an expression contains a function call or channel receive operation } -// lookup looks up name in the current context and returns the matching object, or nil. -func (ctxt *context) lookup(name string) Object { - _, obj := ctxt.scope.LookupParent(name, ctxt.pos) +// lookup looks up name in the current environment and returns the matching object, or nil. +func (env *environment) lookup(name string) Object { + _, obj := env.scope.LookupParent(name, env.pos) return obj } @@ -73,7 +79,29 @@ type importKey struct { // A dotImportKey describes a dot-imported object in the given scope. type dotImportKey struct { scope *Scope - obj Object + name string +} + +// An action describes a (delayed) action. +type action struct { + f func() // action to be executed + desc *actionDesc // action description; may be nil, requires debug to be set +} + +// If debug is set, describef sets a printf-formatted description for action a. +// Otherwise, it is a no-op. +func (a *action) describef(pos positioner, format string, args ...any) { + if debug { + a.desc = &actionDesc{pos, format, args} + } +} + +// An actionDesc provides information on an action. +// For debugging only. +type actionDesc struct { + pos positioner + format string + args []any } // A Checker maintains the state of the type checker. @@ -82,14 +110,15 @@ type Checker struct { // package information // (initialized by NewChecker, valid for the life-time of checker) conf *Config + ctxt *Context // context for de-duplicating instances fset *token.FileSet pkg *Package *Info - version version // accepted language version - objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info - impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions - typMap map[string]*Named // maps an instantiated named type hash to a *Named type + version version // accepted language version + nextID uint64 // unique Id for type parameters (first valid Id is 1) + objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info + impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package + infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection) // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -104,19 +133,24 @@ type Checker struct { // information collected during type-checking of a set of package files // (initialized by Files, valid only for the duration of check.Files; // maps and lists are allocated on demand) - files []*ast.File // package files - imports []*PkgName // list of imported packages - dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through + files []*ast.File // package files + imports []*PkgName // list of imported packages + dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through + recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type + brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types + unionTypeSets map[*Union]*_TypeSet // computed type sets for union types + mono monoGraph // graph for detecting non-monomorphizable instantiation loops firstErr error // first error encountered methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods untyped map[ast.Expr]exprInfo // map of expressions without final type - delayed []func() // stack of delayed action segments; segments are processed in FIFO order + delayed []action // stack of delayed action segments; segments are processed in FIFO order objPath []Object // path of object dependencies during type inference (for cycle reporting) + defTypes []*Named // defined types created during type checking, for final validation. - // context within which the current object is type-checked - // (valid only for the duration of type-checking a specific object) - context + // environment within which the current object is type-checked (valid only + // for the duration of type-checking a specific object) + environment // debugging indent int // indentation for tracing @@ -134,6 +168,27 @@ func (check *Checker) addDeclDep(to Object) { from.addDep(to) } +// brokenAlias records that alias doesn't have a determined type yet. +// It also sets alias.typ to Typ[Invalid]. +func (check *Checker) brokenAlias(alias *TypeName) { + if check.brokenAliases == nil { + check.brokenAliases = make(map[*TypeName]bool) + } + check.brokenAliases[alias] = true + alias.typ = Typ[Invalid] +} + +// validAlias records that alias has the valid type typ (possibly Typ[Invalid]). +func (check *Checker) validAlias(alias *TypeName, typ Type) { + delete(check.brokenAliases, alias) + alias.typ = typ +} + +// isBrokenAlias reports whether alias doesn't have a determined type yet. +func (check *Checker) isBrokenAlias(alias *TypeName) bool { + return alias.typ == Typ[Invalid] && check.brokenAliases[alias] +} + func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { m := check.untyped if m == nil { @@ -147,8 +202,12 @@ func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, ty // either at the end of the current statement, or in case of a local constant // or variable declaration, before the constant or variable is in scope // (so that f still sees the scope before any new declarations). -func (check *Checker) later(f func()) { - check.delayed = append(check.delayed, f) +// later returns the pushed action so one can provide a description +// via action.describef for debugging, if desired. +func (check *Checker) later(f func()) *action { + i := len(check.delayed) + check.delayed = append(check.delayed, action{f: f}) + return &check.delayed[i] } // push pushes obj onto the object path and returns its index in the path. @@ -179,21 +238,21 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch info = new(Info) } - version, err := parseGoVersion(conf.goVersion) + version, err := parseGoVersion(conf.GoVersion) if err != nil { - panic(fmt.Sprintf("invalid Go version %q (%v)", conf.goVersion, err)) + panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err)) } return &Checker{ conf: conf, + ctxt: conf.Context, fset: fset, pkg: pkg, Info: info, version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - posMap: make(map[*Interface][]token.Pos), - typMap: make(map[string]*Named), + infoMap: make(map[*Named]typeInfo), } } @@ -266,6 +325,8 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { check.processDelayed(0) // incl. all functions + check.expandDefTypes() + check.initOrder() if !check.conf.DisableUnusedImportCheck { @@ -274,8 +335,9 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { check.recordUntyped() - if check.Info != nil { - sanitizeInfo(check.Info) + if check.firstErr == nil { + // TODO(mdempsky): Ensure monomorph is safe when errors exist. + check.monomorph() } check.pkg.complete = true @@ -285,6 +347,11 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { check.dotImportMap = nil check.pkgPathMap = nil check.seenPkgMap = nil + check.recvTParamMap = nil + check.brokenAliases = nil + check.unionTypeSets = nil + check.defTypes = nil + check.ctxt = nil // TODO(rFindley) There's more memory we should release at this point. @@ -300,12 +367,40 @@ func (check *Checker) processDelayed(top int) { // add more actions (such as nested functions), so // this is a sufficiently bounded process. for i := top; i < len(check.delayed); i++ { - check.delayed[i]() // may append to check.delayed + a := &check.delayed[i] + if trace && a.desc != nil { + fmt.Println() + check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...) + } + a.f() // may append to check.delayed } assert(top <= len(check.delayed)) // stack must not have shrunk check.delayed = check.delayed[:top] } +func (check *Checker) expandDefTypes() { + // Ensure that every defined type created in the course of type-checking has + // either non-*Named underlying, or is unresolved. + // + // This guarantees that we don't leak any types whose underlying is *Named, + // because any unresolved instances will lazily compute their underlying by + // substituting in the underlying of their origin. The origin must have + // either been imported or type-checked and expanded here, and in either case + // its underlying will be fully expanded. + for i := 0; i < len(check.defTypes); i++ { + n := check.defTypes[i] + switch n.underlying.(type) { + case nil: + if n.resolver == nil { + panic("nil underlying") + } + case *Named: + n.under() // n.under may add entries to check.defTypes + } + n.check = nil + } +} + func (check *Checker) record(x *operand) { // convert x into a user-friendly set of values // TODO(gri) this code can be simplified @@ -355,9 +450,9 @@ func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, } if mode == constant_ { assert(val != nil) - // We check is(typ, IsConstType) here as constant expressions may be + // We check allBasic(typ, IsConstType) here as constant expressions may be // recorded as type parameters. - assert(typ == Typ[Invalid] || is(typ, IsConstType)) + assert(typ == Typ[Invalid] || allBasic(typ, IsConstType)) } if m := check.Types; m != nil { m[x] = TypeAndValue{mode, typ, val} @@ -408,12 +503,38 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { } } -func (check *Checker) recordInferred(call ast.Expr, targs []Type, sig *Signature) { - assert(call != nil) - assert(sig != nil) - if m := getInferred(check.Info); m != nil { - m[call] = _Inferred{targs, sig} +// recordInstance records instantiation information into check.Info, if the +// Instances map is non-nil. The given expr must be an ident, selector, or +// index (list) expr with ident or selector operand. +// +// TODO(rfindley): the expr parameter is fragile. See if we can access the +// instantiated identifier in some other way. +func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) { + ident := instantiatedIdent(expr) + assert(ident != nil) + assert(typ != nil) + if m := check.Instances; m != nil { + m[ident] = Instance{newTypeList(targs), typ} + } +} + +func instantiatedIdent(expr ast.Expr) *ast.Ident { + var selOrIdent ast.Expr + switch e := expr.(type) { + case *ast.IndexExpr: + selOrIdent = e.X + case *ast.IndexListExpr: + selOrIdent = e.X + case *ast.SelectorExpr, *ast.Ident: + selOrIdent = e + } + switch x := selOrIdent.(type) { + case *ast.Ident: + return x + case *ast.SelectorExpr: + return x.Sel } + panic("instantiated ident not found") } func (check *Checker) recordDef(id *ast.Ident, obj Object) { diff --git a/libgo/go/go/types/check_test.go b/libgo/go/go/types/check_test.go index f83abf1..81ea81c 100644 --- a/libgo/go/go/types/check_test.go +++ b/libgo/go/go/types/check_test.go @@ -20,9 +20,6 @@ // _ = x /* ERROR "not declared" */ + 1 // } -// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */ -// and test against strict mode. - package types_test import ( @@ -34,6 +31,7 @@ import ( "go/parser" "go/scanner" "go/token" + "internal/buildcfg" "internal/testenv" "os" "path/filepath" @@ -53,7 +51,7 @@ var ( var fset = token.NewFileSet() // Positioned errors are of the form filename:line:column: message . -var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`) +var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(?s)(.*)`) // splitError splits an error's error message into a position string // and the actual error message. If there's no position information, @@ -202,20 +200,35 @@ func asGoVersion(s string) string { return "" } -func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string, srcs [][]byte, manual bool, imp Importer) { +// excludedForUnifiedBuild lists files that cannot be tested +// when using the unified build's export data. +// TODO(gri) enable as soon as the unified build supports this. +var excludedForUnifiedBuild = map[string]bool{ + "issue47818.go2": true, + "issue49705.go2": true, +} + +func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, manual bool, imp Importer) { if len(filenames) == 0 { t.Fatal("no source files") } - if strings.HasSuffix(filenames[0], ".go2") && !typeparams.Enabled { - t.Skip("type params are not enabled") + if buildcfg.Experiment.Unified { + for _, f := range filenames { + if excludedForUnifiedBuild[filepath.Base(f)] { + t.Logf("%s cannot be tested with unified build - skipped", f) + return + } + } } - if strings.HasSuffix(filenames[0], ".go1") && typeparams.Enabled { + + if strings.HasSuffix(filenames[0], ".go1") { + // TODO(rfindley): re-enable this test by using GoVersion. t.Skip("type params are enabled") } mode := parser.AllErrors - if !strings.HasSuffix(filenames[0], ".go2") { + if !strings.HasSuffix(filenames[0], ".go2") && !manual { mode |= typeparams.DisallowParsing } @@ -228,6 +241,7 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string, } // if no Go version is given, consider the package name + goVersion := *goVersion if goVersion == "" { goVersion = asGoVersion(pkgName) } @@ -243,7 +257,7 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string, // typecheck and collect typechecker errors var conf Config conf.Sizes = sizes - SetGoVersion(&conf, goVersion) + conf.GoVersion = goVersion // special case for importC.src if len(filenames) == 1 { @@ -303,29 +317,48 @@ func checkFiles(t *testing.T, sizes Sizes, goVersion string, filenames []string, } } -// TestManual is for manual testing of input files, provided as a list -// of arguments after the test arguments (and a separating "--"). For -// instance, to check the files foo.go and bar.go, use: +// TestManual is for manual testing of a package - either provided +// as a list of filenames belonging to the package, or a directory +// name containing the package files - after the test arguments +// (and a separating "--"). For instance, to test the package made +// of the files foo.go and bar.go, use: // // go test -run Manual -- foo.go bar.go // -// Provide the -verify flag to verify errors against ERROR comments in -// the input files rather than having a list of errors reported. -// The accepted Go language version can be controlled with the -lang flag. +// If no source arguments are provided, the file testdata/manual.go2 +// is used instead. +// Provide the -verify flag to verify errors against ERROR comments +// in the input files rather than having a list of errors reported. +// The accepted Go language version can be controlled with the -lang +// flag. func TestManual(t *testing.T) { + testenv.MustHaveGoBuild(t) + filenames := flag.Args() if len(filenames) == 0 { - return + filenames = []string{filepath.FromSlash("testdata/manual.go2")} } - testenv.MustHaveGoBuild(t) + + info, err := os.Stat(filenames[0]) + if err != nil { + t.Fatalf("TestManual: %v", err) + } + DefPredeclaredTestFuncs() - testPkg(t, filenames, *goVersion, true) + if info.IsDir() { + if len(filenames) > 1 { + t.Fatal("TestManual: must have only one directory argument") + } + testDir(t, filenames[0], true) + } else { + testPkg(t, filenames, true) + } } func TestLongConstants(t *testing.T) { format := "package longconst\n\nconst _ = %s\nconst _ = %s // ERROR excessively long constant" src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001)) - checkFiles(t, nil, "", []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil) + testFiles(t, nil, []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil) } // TestIndexRepresentability tests that constant index operands must @@ -333,32 +366,28 @@ func TestLongConstants(t *testing.T) { // represent larger values. func TestIndexRepresentability(t *testing.T) { const src = "package index\n\nvar s []byte\nvar _ = s[int64 /* ERROR \"int64\\(1\\) << 40 \\(.*\\) overflows int\" */ (1) << 40]" - checkFiles(t, &StdSizes{4, 4}, "", []string{"index.go"}, [][]byte{[]byte(src)}, false, nil) -} - -func TestIssue46453(t *testing.T) { - if typeparams.Enabled { - t.Skip("type params are enabled") - } - const src = "package p\ntype _ comparable // ERROR \"undeclared name: comparable\"" - checkFiles(t, nil, "", []string{"issue46453.go"}, [][]byte{[]byte(src)}, false, nil) + testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false, nil) } func TestIssue47243_TypedRHS(t *testing.T) { // The RHS of the shift expression below overflows uint on 32bit platforms, // but this is OK as it is explicitly typed. const src = "package issue47243\n\nvar a uint64; var _ = a << uint64(4294967296)" // uint64(1<<32) - checkFiles(t, &StdSizes{4, 4}, "", []string{"p.go"}, [][]byte{[]byte(src)}, false, nil) + testFiles(t, &StdSizes{4, 4}, []string{"p.go"}, [][]byte{[]byte(src)}, false, nil) } -func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "check") } -func TestExamples(t *testing.T) { testDir(t, "examples") } -func TestFixedbugs(t *testing.T) { testDir(t, "fixedbugs") } +func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) } +func TestSpec(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) } +func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) } +func TestFixedbugs(t *testing.T) { + DefPredeclaredTestFuncs() + testDirFiles(t, "testdata/fixedbugs", false) +} -func testDir(t *testing.T, dir string) { +func testDirFiles(t *testing.T, dir string, manual bool) { testenv.MustHaveGoBuild(t) + dir = filepath.FromSlash(dir) - dir = filepath.Join("testdata", dir) fis, err := os.ReadDir(dir) if err != nil { t.Error(err) @@ -368,28 +397,38 @@ func testDir(t *testing.T, dir string) { for _, fi := range fis { path := filepath.Join(dir, fi.Name()) - // if fi is a directory, its files make up a single package - var filenames []string + // If fi is a directory, its files make up a single package. if fi.IsDir() { - fis, err := os.ReadDir(path) - if err != nil { - t.Error(err) - continue - } - for _, fi := range fis { - filenames = append(filenames, filepath.Join(path, fi.Name())) - } + testDir(t, path, manual) } else { - filenames = []string{path} + t.Run(filepath.Base(path), func(t *testing.T) { + testPkg(t, []string{path}, manual) + }) } - t.Run(filepath.Base(path), func(t *testing.T) { - testPkg(t, filenames, "", false) - }) } } +func testDir(t *testing.T, dir string, manual bool) { + testenv.MustHaveGoBuild(t) + + fis, err := os.ReadDir(dir) + if err != nil { + t.Error(err) + return + } + + var filenames []string + for _, fi := range fis { + filenames = append(filenames, filepath.Join(dir, fi.Name())) + } + + t.Run(filepath.Base(dir), func(t *testing.T) { + testPkg(t, filenames, manual) + }) +} + // TODO(rFindley) reconcile the different test setup in go/types with types2. -func testPkg(t *testing.T, filenames []string, goVersion string, manual bool) { +func testPkg(t *testing.T, filenames []string, manual bool) { srcs := make([][]byte, len(filenames)) for i, filename := range filenames { src, err := os.ReadFile(filename) @@ -398,5 +437,5 @@ func testPkg(t *testing.T, filenames []string, goVersion string, manual bool) { } srcs[i] = src } - checkFiles(t, nil, goVersion, filenames, srcs, manual, nil) + testFiles(t, nil, filenames, srcs, manual, nil) } diff --git a/libgo/go/go/types/context.go b/libgo/go/go/types/context.go new file mode 100644 index 0000000..ff4bf89 --- /dev/null +++ b/libgo/go/go/types/context.go @@ -0,0 +1,123 @@ +// 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 types + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "sync" +) + +// An Context is an opaque type checking context. It may be used to share +// identical type instances across type-checked packages or calls to +// Instantiate. +// +// It is safe for concurrent use. +type Context struct { + mu sync.Mutex + typeMap map[string][]ctxtEntry // type hash -> instances entries + nextID int // next unique ID + originIDs map[Type]int // origin type -> unique ID +} + +type ctxtEntry struct { + orig Type + targs []Type + instance Type // = orig[targs] +} + +// NewContext creates a new Context. +func NewContext() *Context { + return &Context{ + typeMap: make(map[string][]ctxtEntry), + originIDs: make(map[Type]int), + } +} + +// instanceHash returns a string representation of typ instantiated with targs. +// The hash should be a perfect hash, though out of caution the type checker +// does not assume this. The result is guaranteed to not contain blanks. +func (ctxt *Context) instanceHash(orig Type, targs []Type) string { + assert(ctxt != nil) + assert(orig != nil) + var buf bytes.Buffer + + h := newTypeHasher(&buf, ctxt) + h.string(strconv.Itoa(ctxt.getID(orig))) + // Because we've already written the unique origin ID this call to h.typ is + // unnecessary, but we leave it for hash readability. It can be removed later + // if performance is an issue. + h.typ(orig) + if len(targs) > 0 { + // TODO(rfindley): consider asserting on isGeneric(typ) here, if and when + // isGeneric handles *Signature types. + h.typeList(targs) + } + + return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 +} + +// lookup returns an existing instantiation of orig with targs, if it exists. +// Otherwise, it returns nil. +func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type { + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + + for _, e := range ctxt.typeMap[h] { + if identicalInstance(orig, targs, e.orig, e.targs) { + return e.instance + } + if debug { + // Panic during development to surface any imperfections in our hash. + panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance)) + } + } + + return nil +} + +// update de-duplicates n against previously seen types with the hash h. If an +// identical type is found with the type hash h, the previously seen type is +// returned. Otherwise, n is returned, and recorded in the Context for the hash +// h. +func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type { + assert(inst != nil) + + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + + for _, e := range ctxt.typeMap[h] { + if inst == nil || Identical(inst, e.instance) { + return e.instance + } + if debug { + // Panic during development to surface any imperfections in our hash. + panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance)) + } + } + + ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{ + orig: orig, + targs: targs, + instance: inst, + }) + + return inst +} + +// getID returns a unique ID for the type t. +func (ctxt *Context) getID(t Type) int { + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + id, ok := ctxt.originIDs[t] + if !ok { + id = ctxt.nextID + ctxt.originIDs[t] = id + ctxt.nextID++ + } + return id +} diff --git a/libgo/go/go/types/context_test.go b/libgo/go/go/types/context_test.go new file mode 100644 index 0000000..ec300502 --- /dev/null +++ b/libgo/go/go/types/context_test.go @@ -0,0 +1,70 @@ +// 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 types + +import ( + "go/token" + "testing" +) + +func TestContextHashCollisions(t *testing.T) { + if debug { + t.Skip("hash collisions are expected, and would fail debug assertions") + } + // Unit test the de-duplication fall-back logic in Context. + // + // We can't test this via Instantiate because this is only a fall-back in + // case our hash is imperfect. + // + // These lookups and updates use reasonable looking types in an attempt to + // make them robust to internal type assertions, but could equally well use + // arbitrary types. + + // Create some distinct origin types. nullaryP and nullaryQ have no + // parameters and are identical (but have different type parameter names). + // unaryP has a parameter. + var nullaryP, nullaryQ, unaryP Type + { + // type nullaryP = func[P any]() + tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "P", nil), &emptyInterface) + nullaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false) + } + { + // type nullaryQ = func[Q any]() + tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "Q", nil), &emptyInterface) + nullaryQ = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false) + } + { + // type unaryP = func[P any](_ P) + tparam := NewTypeParam(NewTypeName(token.NoPos, nil, "P", nil), &emptyInterface) + params := NewTuple(NewVar(token.NoPos, nil, "_", tparam)) + unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false) + } + + ctxt := NewContext() + + // Update the context with an instantiation of nullaryP. + inst := NewSignatureType(nil, nil, nil, nil, nil, false) + if got := ctxt.update("", nullaryP, []Type{Typ[Int]}, inst); got != inst { + t.Error("bad") + } + + // unaryP is not identical to nullaryP, so we should not get inst when + // instantiated with identical type arguments. + if got := ctxt.lookup("", unaryP, []Type{Typ[Int]}); got != nil { + t.Error("bad") + } + + // nullaryQ is identical to nullaryP, so we *should* get inst when + // instantiated with identical type arguments. + if got := ctxt.lookup("", nullaryQ, []Type{Typ[Int]}); got != inst { + t.Error("bad") + } + + // ...but verify we don't get inst with different type arguments. + if got := ctxt.lookup("", nullaryQ, []Type{Typ[String]}); got != nil { + t.Error("bad") + } +} diff --git a/libgo/go/go/types/conversions.go b/libgo/go/go/types/conversions.go index ad6d3ee..8474135 100644 --- a/libgo/go/go/types/conversions.go +++ b/libgo/go/go/types/conversions.go @@ -16,33 +16,71 @@ import ( func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant_ - var ok bool - var reason string - switch { - case constArg && isConstType(T): - // constant conversion - switch t := asBasic(T); { - case representableConst(x.val, check, t, &x.val): - ok = true + constConvertibleTo := func(T Type, val *constant.Value) bool { + switch t, _ := under(T).(*Basic); { + case t == nil: + // nothing to do + case representableConst(x.val, check, t, val): + return true case isInteger(x.typ) && isString(t): codepoint := unicode.ReplacementChar if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune { codepoint = rune(i) } - x.val = constant.MakeString(string(codepoint)) - ok = true + if val != nil { + *val = constant.MakeString(string(codepoint)) + } + return true } - case x.convertibleTo(check, T, &reason): + return false + } + + var ok bool + var cause string + switch { + case constArg && isConstType(T): + // constant conversion + ok = constConvertibleTo(T, &x.val) + case constArg && isTypeParam(T): + // x is convertible to T if it is convertible + // to each specific type in the type set of T. + // If T's type set is empty, or if it doesn't + // have specific types, constant x cannot be + // converted. + ok = T.(*TypeParam).underIs(func(u Type) bool { + // t is nil if there are no specific type terms + if u == nil { + cause = check.sprintf("%s does not contain specific types", T) + return false + } + if !constConvertibleTo(u, nil) { + cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T) + return false + } + return true + }) + x.mode = value // type parameters are not constants + case x.convertibleTo(check, T, &cause): // non-constant conversion - x.mode = value ok = true + x.mode = value } if !ok { - if reason != "" { - check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, reason) + // TODO(rfindley): use types2-style error reporting here. + if compilerErrorMessages { + if cause != "" { + // Add colon at end of line if we have a following cause. + check.errorf(x, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", x, T, cause) + } else { + check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T) + } } else { - check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T) + if cause != "" { + check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, cause) + } else { + check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T) + } } x.mode = invalid return @@ -58,11 +96,11 @@ func (check *Checker) conversion(x *operand, T Type) { // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. - // - For integer to string conversions, keep the argument type. + // - For constant integer to string conversions, keep the argument type. // (See also the TODO below.) - if IsInterface(T) || constArg && !isConstType(T) || x.isNil() { + if IsInterface(T) && !isTypeParam(T) || constArg && !isConstType(T) || x.isNil() { final = Default(x.typ) // default type of untyped nil is untyped nil - } else if isInteger(x.typ) && isString(T) { + } else if x.mode == constant_ && isInteger(x.typ) && allString(T) { final = x.typ } check.updateExprType(x.expr, final, true) @@ -81,104 +119,168 @@ func (check *Checker) conversion(x *operand, T Type) { // is tricky because we'd have to run updateExprType on the argument first. // (Issue #21982.) -// convertibleTo reports whether T(x) is valid. +// convertibleTo reports whether T(x) is valid. In the failure case, *cause +// may be set to the cause for the failure. // The check parameter may be nil if convertibleTo is invoked through an // exported API call, i.e., when all methods have been type-checked. -func (x *operand) convertibleTo(check *Checker, T Type, reason *string) bool { +func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool { // "x is assignable to T" - if ok, _ := x.assignableTo(check, T, nil); ok { + if ok, _ := x.assignableTo(check, T, cause); ok { return true } - // "x's type and T have identical underlying types if tags are ignored" + // "V and T have identical underlying types if tags are ignored + // and V and T are not type parameters" V := x.typ Vu := under(V) Tu := under(T) - if check.identicalIgnoreTags(Vu, Tu) { + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) + if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil { return true } - // "x's type and T are unnamed pointer types and their pointer base types - // have identical underlying types if tags are ignored" + // "V and T are unnamed pointer types and their pointer base types + // have identical underlying types if tags are ignored + // and their pointer base types are not type parameters" if V, ok := V.(*Pointer); ok { if T, ok := T.(*Pointer); ok { - if check.identicalIgnoreTags(under(V.base), under(T.base)) { + if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) { return true } } } - // "x's type and T are both integer or floating point types" - if isIntegerOrFloat(V) && isIntegerOrFloat(T) { + // "V and T are both integer or floating point types" + if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) { return true } - // "x's type and T are both complex types" - if isComplex(V) && isComplex(T) { + // "V and T are both complex types" + if isComplex(Vu) && isComplex(Tu) { return true } - // "x is an integer or a slice of bytes or runes and T is a string type" - if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { + // "V is an integer or a slice of bytes or runes and T is a string type" + if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) { return true } - // "x is a string and T is a slice of bytes or runes" - if isString(V) && isBytesOrRunes(Tu) { + // "V is a string and T is a slice of bytes or runes" + if isString(Vu) && isBytesOrRunes(Tu) { return true } // package unsafe: // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" - if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { + if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) { return true } // "and vice versa" - if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { + if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) { return true } - // "x is a slice, T is a pointer-to-array type, + // "V a slice, T is a pointer-to-array type, // and the slice and array types have identical element types." - if s := asSlice(V); s != nil { - if p := asPointer(T); p != nil { - if a := asArray(p.Elem()); a != nil { - if check.identical(s.Elem(), a.Elem()) { + if s, _ := Vu.(*Slice); s != nil { + if p, _ := Tu.(*Pointer); p != nil { + if a, _ := under(p.Elem()).(*Array); a != nil { + if Identical(s.Elem(), a.Elem()) { if check == nil || check.allowVersion(check.pkg, 1, 17) { return true } - if reason != nil { - *reason = "conversion of slices to array pointers requires go1.17 or later" + if cause != nil { + *cause = "conversion of slices to array pointers requires go1.17 or later" } } } } } + // optimization: if we don't have type parameters, we're done + if Vp == nil && Tp == nil { + return false + } + + errorf := func(format string, args ...any) { + if check != nil && cause != nil { + msg := check.sprintf(format, args...) + if *cause != "" { + msg += "\n\t" + *cause + } + *cause = msg + } + } + + // generic cases with specific type terms + // (generic operands cannot be constants, so we can ignore x.val) + switch { + case Vp != nil && Tp != nil: + x := *x // don't clobber outer x + return Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + return Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + if !x.convertibleTo(check, T.typ, cause) { + errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp) + return false + } + return true + }) + }) + case Vp != nil: + x := *x // don't clobber outer x + return Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + if !x.convertibleTo(check, T, cause) { + errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T) + return false + } + return true + }) + case Tp != nil: + return Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + if !x.convertibleTo(check, T.typ, cause) { + errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp) + return false + } + return true + }) + } + return false } func isUintptr(typ Type) bool { - t := asBasic(typ) + t, _ := under(typ).(*Basic) return t != nil && t.kind == Uintptr } func isUnsafePointer(typ Type) bool { - // TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct? - // (The former calls under(), while the latter doesn't.) - // The spec does not say so, but gc claims it is. See also - // issue 6326. - t := asBasic(typ) + t, _ := under(typ).(*Basic) return t != nil && t.kind == UnsafePointer } func isPointer(typ Type) bool { - return asPointer(typ) != nil + _, ok := under(typ).(*Pointer) + return ok } func isBytesOrRunes(typ Type) bool { - if s := asSlice(typ); s != nil { - t := asBasic(s.elem) + if s, _ := under(typ).(*Slice); s != nil { + t, _ := under(s.elem).(*Basic) return t != nil && (t.kind == Byte || t.kind == Rune) } return false diff --git a/libgo/go/go/types/decl.go b/libgo/go/go/types/decl.go index 9211feb..cd6f709a 100644 --- a/libgo/go/go/types/decl.go +++ b/libgo/go/go/types/decl.go @@ -8,7 +8,6 @@ import ( "fmt" "go/ast" "go/constant" - "go/internal/typeparams" "go/token" ) @@ -51,7 +50,7 @@ func pathString(path []Object) string { return s } -// objDecl type-checks the declaration of obj in its respective (file) context. +// objDecl type-checks the declaration of obj in its respective (file) environment. // For the meaning of def, see Checker.definedType, in typexpr.go. func (check *Checker) objDecl(obj Object, def *Named) { if trace && obj.Type() == nil { @@ -118,7 +117,7 @@ func (check *Checker) objDecl(obj Object, def *Named) { fallthrough case grey: - // We have a cycle. + // We have a (possibly invalid) cycle. // In the existing code, this is marked by a non-nil type // for the object except for constants and variables whose // type may be non-nil (known), or nil if it depends on the @@ -130,17 +129,17 @@ func (check *Checker) objDecl(obj Object, def *Named) { // order code. switch obj := obj.(type) { case *Const: - if check.cycle(obj) || obj.typ == nil { + if !check.validCycle(obj) || obj.typ == nil { obj.typ = Typ[Invalid] } case *Var: - if check.cycle(obj) || obj.typ == nil { + if !check.validCycle(obj) || obj.typ == nil { obj.typ = Typ[Invalid] } case *TypeName: - if check.cycle(obj) { + if !check.validCycle(obj) { // break cycle // (without this, calling underlying() // below may lead to an endless loop @@ -150,7 +149,7 @@ func (check *Checker) objDecl(obj Object, def *Named) { } case *Func: - if check.cycle(obj) { + if !check.validCycle(obj) { // Don't set obj.typ to Typ[Invalid] here // because plenty of code type-asserts that // functions have a *Signature type. Grey @@ -172,11 +171,11 @@ func (check *Checker) objDecl(obj Object, def *Named) { unreachable() } - // save/restore current context and setup object context - defer func(ctxt context) { - check.context = ctxt - }(check.context) - check.context = context{ + // save/restore current environment and set up object environment + defer func(env environment) { + check.environment = env + }(check.environment) + check.environment = environment{ scope: d.file, } @@ -204,9 +203,9 @@ func (check *Checker) objDecl(obj Object, def *Named) { } } -// cycle checks if the cycle starting with obj is valid and +// validCycle checks if the cycle starting with obj is valid and // reports an error if it is not. -func (check *Checker) cycle(obj Object) (isCycle bool) { +func (check *Checker) validCycle(obj Object) (valid bool) { // The object map contains the package scope objects and the non-interface methods. if debug { info := check.objMap[obj] @@ -222,13 +221,23 @@ func (check *Checker) cycle(obj Object) (isCycle bool) { assert(obj.color() >= grey) start := obj.color() - grey // index of obj in objPath cycle := check.objPath[start:] - nval := 0 // number of (constant or variable) values in the cycle - ndef := 0 // number of type definitions in the cycle + tparCycle := false // if set, the cycle is through a type parameter list + nval := 0 // number of (constant or variable) values in the cycle; valid if !generic + ndef := 0 // number of type definitions in the cycle; valid if !generic +loop: for _, obj := range cycle { switch obj := obj.(type) { case *Const, *Var: nval++ case *TypeName: + // If we reach a generic type that is part of a cycle + // and we are in a type parameter list, we have a cycle + // through a type parameter list, which is invalid. + if check.inTParamList && isGeneric(obj.typ) { + tparCycle = true + break loop + } + // Determine if the type name is an alias or not. For // package-level objects, use the object map which // provides syntactic information (which doesn't rely @@ -256,105 +265,38 @@ func (check *Checker) cycle(obj Object) (isCycle bool) { if trace { check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle)) - check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef) + if tparCycle { + check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list") + } else { + check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef) + } defer func() { - if isCycle { + if valid { + check.trace(obj.Pos(), "=> cycle is valid") + } else { check.trace(obj.Pos(), "=> error: cycle is invalid") } }() } - // A cycle involving only constants and variables is invalid but we - // ignore them here because they are reported via the initialization - // cycle check. - if nval == len(cycle) { - return false - } - - // A cycle involving only types (and possibly functions) must have at least - // one type definition to be permitted: If there is no type definition, we - // have a sequence of alias type names which will expand ad infinitum. - if nval == 0 && ndef > 0 { - return false // cycle is permitted - } - - check.cycleError(cycle) - - return true -} - -type typeInfo uint - -// validType verifies that the given type does not "expand" infinitely -// producing a cycle in the type graph. Cycles are detected by marking -// defined types. -// (Cycles involving alias types, as in "type A = [10]A" are detected -// earlier, via the objDecl cycle detection mechanism.) -func (check *Checker) validType(typ Type, path []Object) typeInfo { - const ( - unknown typeInfo = iota - marked - valid - invalid - ) - - switch t := typ.(type) { - case *Array: - return check.validType(t.elem, path) - - case *Struct: - for _, f := range t.fields { - if check.validType(f.typ, path) == invalid { - return invalid - } - } - - case *Interface: - for _, etyp := range t.embeddeds { - if check.validType(etyp, path) == invalid { - return invalid - } - } - - case *Named: - // don't touch the type if it is from a different package or the Universe scope - // (doing so would lead to a race condition - was issue #35049) - if t.obj.pkg != check.pkg { - return valid + if !tparCycle { + // A cycle involving only constants and variables is invalid but we + // ignore them here because they are reported via the initialization + // cycle check. + if nval == len(cycle) { + return true } - // don't report a 2nd error if we already know the type is invalid - // (e.g., if a cycle was detected earlier, via under). - if t.underlying == Typ[Invalid] { - t.info = invalid - return invalid + // A cycle involving only types (and possibly functions) must have at least + // one type definition to be permitted: If there is no type definition, we + // have a sequence of alias type names which will expand ad infinitum. + if nval == 0 && ndef > 0 { + return true } - - switch t.info { - case unknown: - t.info = marked - t.info = check.validType(t.orig, append(path, t.obj)) // only types of current package added to path - case marked: - // cycle detected - for i, tn := range path { - if t.obj.pkg != check.pkg { - panic("internal error: type cycle via package-external type") - } - if tn == t.obj { - check.cycleError(path[i:]) - t.info = invalid - return t.info - } - } - panic("internal error: cycle start not found") - } - return t.info - - case *instance: - return check.validType(t.expand(), path) } - return valid + check.cycleError(cycle) + return false } // cycleError reports a declaration cycle starting with @@ -365,7 +307,16 @@ func (check *Checker) cycleError(cycle []Object) { // cycle? That would be more consistent with other error messages. i := firstInSrc(cycle) obj := cycle[i] - check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name()) + // If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors. + tname, _ := obj.(*TypeName) + if tname != nil && tname.IsAlias() { + check.validAlias(tname, Typ[Invalid]) + } + if tname != nil && compilerErrorMessages { + check.errorf(obj, _InvalidDeclCycle, "invalid recursive type %s", obj.Name()) + } else { + check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name()) + } for range cycle { check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented i++ @@ -566,172 +517,87 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { } } - check.initVars(lhs, []ast.Expr{init}, token.NoPos) -} - -// under returns the expanded underlying type of n0; possibly by following -// forward chains of named types. If an underlying type is found, resolve -// the chain by setting the underlying type for each defined type in the -// chain before returning it. If no underlying type is found or a cycle -// is detected, the result is Typ[Invalid]. If a cycle is detected and -// n0.check != nil, the cycle is reported. -func (n0 *Named) under() Type { - u := n0.underlying - - if u == Typ[Invalid] { - return u - } - - // If the underlying type of a defined type is not a defined - // (incl. instance) type, then that is the desired underlying - // type. - switch u.(type) { - case nil: - return Typ[Invalid] - default: - // common case - return u - case *Named, *instance: - // handled below - } - - if n0.check == nil { - panic("internal error: Named.check == nil but type is incomplete") - } - - // Invariant: after this point n0 as well as any named types in its - // underlying chain should be set up when this function exits. - check := n0.check - - // If we can't expand u at this point, it is invalid. - n := asNamed(u) - if n == nil { - n0.underlying = Typ[Invalid] - return n0.underlying - } - - // Otherwise, follow the forward chain. - seen := map[*Named]int{n0: 0} - path := []Object{n0.obj} - for { - u = n.underlying - if u == nil { - u = Typ[Invalid] - break - } - var n1 *Named - switch u1 := u.(type) { - case *Named: - n1 = u1 - case *instance: - n1, _ = u1.expand().(*Named) - if n1 == nil { - u = Typ[Invalid] - } - } - if n1 == nil { - break // end of chain - } - - seen[n] = len(seen) - path = append(path, n.obj) - n = n1 - - if i, ok := seen[n]; ok { - // cycle - check.cycleError(path[i:]) - u = Typ[Invalid] - break - } - } - - for n := range seen { - // We should never have to update the underlying type of an imported type; - // those underlying types should have been resolved during the import. - // Also, doing so would lead to a race condition (was issue #31749). - // Do this check always, not just in debug mode (it's cheap). - if n.obj.pkg != check.pkg { - panic("internal error: imported type with unresolved underlying type") - } - n.underlying = u - } - - return u + check.initVars(lhs, []ast.Expr{init}, nil) } -func (n *Named) setUnderlying(typ Type) { - if n != nil { - n.underlying = typ +// isImportedConstraint reports whether typ is an imported type constraint. +func (check *Checker) isImportedConstraint(typ Type) bool { + named, _ := typ.(*Named) + if named == nil || named.obj.pkg == check.pkg || named.obj.pkg == nil { + return false } + u, _ := named.under().(*Interface) + return u != nil && !u.IsMethodSet() } func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { assert(obj.typ == nil) + var rhs Type check.later(func() { - check.validType(obj.typ, nil) - }) + if t, _ := obj.typ.(*Named); t != nil { // type may be invalid + check.validType(t) + } + // If typ is local, an error was already reported where typ is specified/defined. + if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) { + check.errorf(tdecl.Type, _UnsupportedFeature, "using type constraint %s requires go1.18 or later", rhs) + } + }).describef(obj, "validType(%s)", obj.Name()) alias := tdecl.Assign.IsValid() - if alias && typeparams.Get(tdecl) != nil { + if alias && tdecl.TypeParams.NumFields() != 0 { // The parser will ensure this but we may still get an invalid AST. // Complain and continue as regular type definition. - check.error(atPos(tdecl.Assign), 0, "generic type cannot be alias") + check.error(atPos(tdecl.Assign), _BadDecl, "generic type cannot be alias") alias = false } + // alias declaration if alias { - // type alias declaration if !check.allowVersion(check.pkg, 1, 9) { check.errorf(atPos(tdecl.Assign), _BadDecl, "type aliases requires go1.9 or later") } - obj.typ = Typ[Invalid] - obj.typ = check.anyType(tdecl.Type) - - } else { - // defined type declaration + check.brokenAlias(obj) + rhs = check.varType(tdecl.Type) + check.validAlias(obj, rhs) + return + } - named := check.newNamed(obj, nil, nil) - def.setUnderlying(named) - obj.typ = named // make sure recursive type declarations terminate + // type definition or generic type declaration + named := check.newNamed(obj, nil, nil, nil, nil) + def.setUnderlying(named) - if tparams := typeparams.Get(tdecl); tparams != nil { - check.openScope(tdecl, "type parameters") - defer check.closeScope() - named.tparams = check.collectTypeParams(tparams) - } + if tdecl.TypeParams != nil { + check.openScope(tdecl, "type parameters") + defer check.closeScope() + check.collectTypeParams(&named.tparams, tdecl.TypeParams) + } - // determine underlying type of named - named.orig = check.definedType(tdecl.Type, named) + // determine underlying type of named + rhs = check.definedType(tdecl.Type, named) + assert(rhs != nil) + named.fromRHS = rhs - // The underlying type of named may be itself a named type that is - // incomplete: - // - // type ( - // A B - // B *C - // C A - // ) - // - // The type of C is the (named) type of A which is incomplete, - // and which has as its underlying type the named type B. - // Determine the (final, unnamed) underlying type by resolving - // any forward chain. - // TODO(gri) Investigate if we can just use named.origin here - // and rely on lazy computation of the underlying type. - named.underlying = under(named) + // If the underlying was not set while type-checking the right-hand side, it + // is invalid and an error should have been reported elsewhere. + if named.underlying == nil { + named.underlying = Typ[Invalid] } -} - -func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeName) { - // Type parameter lists should not be empty. The parser will - // complain but we still may get an incorrect AST: ignore it. - if list.NumFields() == 0 { - return + // Disallow a lone type parameter as the RHS of a type declaration (issue #45639). + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if the RHS is a lone type parameter, we will + // use its underlying type (like we do for any RHS in a type declaration), and its + // underlying type is an interface and the type declaration is well defined. + if isTypeParam(rhs) { + check.error(tdecl.Type, _MisplacedTypeParam, "cannot use a type parameter as RHS in type declaration") + named.underlying = Typ[Invalid] } +} +func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) { + var tparams []*TypeParam // Declare type parameters up-front, with empty interface as type bound. // The scope of type parameters starts at the beginning of the type parameter // list (so we can have mutually recursive parameterized interfaces). @@ -739,53 +605,85 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam tparams = check.declareTypeParams(tparams, f.Names) } - setBoundAt := func(at int, bound Type) { - assert(IsInterface(bound)) - tparams[at].typ.(*_TypeParam).bound = bound - } + // Set the type parameters before collecting the type constraints because + // the parameterized type may be used by the constraints (issue #47887). + // Example: type T[P T[P]] interface{} + *dst = bindTParams(tparams) + + // Signal to cycle detection that we are in a type parameter list. + // We can only be inside one type parameter list at any given time: + // function closures may appear inside a type parameter list but they + // cannot be generic, and their bodies are processed in delayed and + // sequential fashion. Note that with each new declaration, we save + // the existing environment and restore it when done; thus inTPList is + // true exactly only when we are in a specific type parameter list. + assert(!check.inTParamList) + check.inTParamList = true + defer func() { + check.inTParamList = false + }() index := 0 - var bound Type + var bounds []Type for _, f := range list.List { - if f.Type == nil { - goto next - } - - // The predeclared identifier "any" is visible only as a constraint - // in a type parameter list. Look for it before general constraint - // resolution. - if tident, _ := unparen(f.Type).(*ast.Ident); tident != nil && tident.Name == "any" && check.lookup("any") == nil { - bound = universeAny + var bound Type + // NOTE: we may be able to assert that f.Type != nil here, but this is not + // an invariant of the AST, so we are cautious. + if f.Type != nil { + bound = check.bound(f.Type) + if isTypeParam(bound) { + // We may be able to allow this since it is now well-defined what + // the underlying type and thus type set of a type parameter is. + // But we may need some additional form of cycle detection within + // type parameter lists. + check.error(f.Type, _MisplacedTypeParam, "cannot use a type parameter as constraint") + bound = Typ[Invalid] + } } else { - bound = check.typ(f.Type) + bound = Typ[Invalid] } - - // type bound must be an interface - // TODO(gri) We should delay the interface check because - // we may not have a complete interface yet: - // type C(type T C) interface {} - // (issue #39724). - if _, ok := under(bound).(*Interface); ok { - // Otherwise, set the bound for each type parameter. - for i := range f.Names { - setBoundAt(index+i, bound) - } - } else if bound != Typ[Invalid] { - check.errorf(f.Type, _Todo, "%s is not an interface", bound) + bounds = append(bounds, bound) + for i := range f.Names { + tparams[index+i].bound = bound } - - next: index += len(f.Names) } +} - return +func (check *Checker) bound(x ast.Expr) Type { + // A type set literal of the form ~T and A|B may only appear as constraint; + // embed it in an implicit interface so that only interface type-checking + // needs to take care of such type expressions. + wrap := false + switch op := x.(type) { + case *ast.UnaryExpr: + wrap = op.Op == token.TILDE + case *ast.BinaryExpr: + wrap = op.Op == token.OR + } + if wrap { + x = &ast.InterfaceType{Methods: &ast.FieldList{List: []*ast.Field{{Type: x}}}} + t := check.typ(x) + // mark t as implicit interface if all went well + if t, _ := t.(*Interface); t != nil { + t.implicit = true + } + return t + } + return check.typ(x) } -func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName { +func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam { + // Use Typ[Invalid] for the type constraint to ensure that a type + // is present even if the actual constraint has not been assigned + // yet. + // TODO(gri) Need to systematically review all uses of type parameter + // constraints to make sure we don't rely on them if they + // are not properly set yet. for _, name := range names { - tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil) - check.newTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect - check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position + tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil) + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect + check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position tparams = append(tparams, tpar) } @@ -813,9 +711,11 @@ func (check *Checker) collectMethods(obj *TypeName) { // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." - base := asNamed(obj.typ) // shouldn't fail but be conservative + base, _ := obj.typ.(*Named) // shouldn't fail but be conservative if base != nil { - if t, _ := base.underlying.(*Struct); t != nil { + assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type + u := base.under() + if t, _ := u.(*Struct); t != nil { for _, fld := range t.fields { if fld.name != "_" { assert(mset.insert(fld) == nil) @@ -826,7 +726,8 @@ func (check *Checker) collectMethods(obj *TypeName) { // Checker.Files may be called multiple times; additional package files // may add methods to already type-checked types. Add pre-existing methods // so that we can detect redeclarations. - for _, m := range base.methods { + for i := 0; i < base.methods.Len(); i++ { + m := base.methods.At(i, nil) assert(m.name != "_") assert(mset.insert(m) == nil) } @@ -851,7 +752,8 @@ func (check *Checker) collectMethods(obj *TypeName) { } if base != nil { - base.methods = append(base.methods, m) + base.resolve(nil) // TODO(mdempsky): Probably unnecessary. + base.AddMethod(m) } } } @@ -877,6 +779,10 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { check.funcType(sig, fdecl.Recv, fdecl.Type) obj.color_ = saved + if fdecl.Type.TypeParams.NumFields() > 0 && fdecl.Body == nil { + check.softErrorf(fdecl.Name, _BadDecl, "parameterized function is missing function body") + } + // function body must be type-checked after global declarations // (functions implemented elsewhere have no body) if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { diff --git a/libgo/go/go/types/errorcodes.go b/libgo/go/go/types/errorcodes.go index 3d24da7..b3796e8 100644 --- a/libgo/go/go/types/errorcodes.go +++ b/libgo/go/go/types/errorcodes.go @@ -281,16 +281,7 @@ const ( _IncomparableMapKey // _InvalidIfaceEmbed occurs when a non-interface type is embedded in an - // interface. - // - // Example: - // type T struct {} - // - // func (T) m() - // - // type I interface { - // T - // } + // interface (for go 1.17 or earlier). _InvalidIfaceEmbed // _InvalidPtrEmbed occurs when an embedded field is of the pointer form *T, @@ -884,7 +875,7 @@ const ( // context in which it is used. // // Example: - // var _ = 1 + "" + // var _ = 1 + new(int) _InvalidUntypedConversion // _BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument @@ -1310,7 +1301,103 @@ const ( // var _ = unsafe.Slice(&x, uint64(1) << 63) _InvalidUnsafeSlice - // _Todo is a placeholder for error codes that have not been decided. - // TODO(rFindley) remove this error code after deciding on errors for generics code. - _Todo + // All codes below were added in Go 1.18. + + // _UnsupportedFeature occurs when a language feature is used that is not + // supported at this Go version. + _UnsupportedFeature + + // _NotAGenericType occurs when a non-generic type is used where a generic + // type is expected: in type or function instantiation. + // + // Example: + // type T int + // + // var _ T[int] + _NotAGenericType + + // _WrongTypeArgCount occurs when a type or function is instantiated with an + // incorrent number of type arguments, including when a generic type or + // function is used without instantiation. + // + // Errors inolving failed type inference are assigned other error codes. + // + // Example: + // type T[p any] int + // + // var _ T[int, string] + // + // Example: + // func f[T any]() {} + // + // var x = f + _WrongTypeArgCount + + // _CannotInferTypeArgs occurs when type or function type argument inference + // fails to infer all type arguments. + // + // Example: + // func f[T any]() {} + // + // func _() { + // f() + // } + // + // Example: + // type N[P, Q any] struct{} + // + // var _ N[int] + _CannotInferTypeArgs + + // _InvalidTypeArg occurs when a type argument does not satisfy its + // corresponding type parameter constraints. + // + // Example: + // type T[P ~int] struct{} + // + // var _ T[string] + _InvalidTypeArg // arguments? InferenceFailed + + // _InvalidInstanceCycle occurs when an invalid cycle is detected + // within the instantiation graph. + // + // Example: + // func f[T any]() { f[*T]() } + _InvalidInstanceCycle + + // _InvalidUnion occurs when an embedded union or approximation element is + // not valid. + // + // Example: + // type _ interface { + // ~int | interface{ m() } + // } + _InvalidUnion + + // _MisplacedConstraintIface occurs when a constraint-type interface is used + // outside of constraint position. + // + // Example: + // type I interface { ~int } + // + // var _ I + _MisplacedConstraintIface + + // _InvalidMethodTypeParams occurs when methods have type parameters. + // + // Example: + // type T int + // + // func (T) m[P any]() {} + _InvalidMethodTypeParams + + // _MisplacedTypeParam occurs when a type parameter is used in a place where + // it is not permitted. + // + // Example: + // type T[P any] P + // + // Example: + // type T[P any] struct{ *P } + _MisplacedTypeParam ) diff --git a/libgo/go/go/types/errorcodes_test.go b/libgo/go/go/types/errorcodes_test.go index 9652920..7ce0158 100644 --- a/libgo/go/go/types/errorcodes_test.go +++ b/libgo/go/go/types/errorcodes_test.go @@ -173,8 +173,10 @@ func TestErrorCodeStyle(t *testing.T) { } } doc := spec.Doc.Text() - if !strings.HasPrefix(doc, name) { - t.Errorf("doc for %q does not start with identifier", name) + if doc == "" { + t.Errorf("%q is undocumented", name) + } else if !strings.HasPrefix(doc, name) { + t.Errorf("doc for %q does not start with the error code name", name) } lowerComment := strings.ToLower(strings.TrimPrefix(doc, name)) for _, bad := range forbiddenInComment { diff --git a/libgo/go/go/types/errors.go b/libgo/go/go/types/errors.go index 2263106..ce62a8c 100644 --- a/libgo/go/go/types/errors.go +++ b/libgo/go/go/types/errors.go @@ -7,6 +7,7 @@ package types import ( + "bytes" "errors" "fmt" "go/ast" @@ -62,40 +63,63 @@ func (check *Checker) markImports(pkg *Package) { } } -func (check *Checker) sprintf(format string, args ...interface{}) string { +func (check *Checker) sprintf(format string, args ...any) string { + return sprintf(check.fset, check.qualifier, false, format, args...) +} + +func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string { for i, arg := range args { switch a := arg.(type) { case nil: arg = "<nil>" case operand: - panic("internal error: should always pass *operand") + panic("got operand instead of *operand") case *operand: - arg = operandString(a, check.qualifier) + arg = operandString(a, qf) case token.Pos: - arg = check.fset.Position(a).String() + if fset != nil { + arg = fset.Position(a).String() + } case ast.Expr: arg = ExprString(a) + case []ast.Expr: + var buf bytes.Buffer + buf.WriteByte('[') + writeExprList(&buf, a) + buf.WriteByte(']') + arg = buf.String() case Object: - arg = ObjectString(a, check.qualifier) + arg = ObjectString(a, qf) case Type: - arg = TypeString(a, check.qualifier) + arg = typeString(a, qf, debug) + case []Type: + var buf bytes.Buffer + buf.WriteByte('[') + for i, x := range a { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(typeString(x, qf, debug)) + } + buf.WriteByte(']') + arg = buf.String() } args[i] = arg } return fmt.Sprintf(format, args...) } -func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) { +func (check *Checker) trace(pos token.Pos, format string, args ...any) { fmt.Printf("%s:\t%s%s\n", check.fset.Position(pos), strings.Repeat(". ", check.indent), - check.sprintf(format, args...), + sprintf(check.fset, check.qualifier, true, format, args...), ) } // dump is only needed for debugging -func (check *Checker) dump(format string, args ...interface{}) { - fmt.Println(check.sprintf(format, args...)) +func (check *Checker) dump(format string, args ...any) { + fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...)) } func (check *Checker) err(err error) { @@ -164,7 +188,7 @@ func (check *Checker) newError(at positioner, code errorCode, soft bool, msg str } // newErrorf creates a new Error, but does not handle it. -func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error { +func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...any) error { msg := check.sprintf(format, args...) return check.newError(at, code, soft, msg) } @@ -173,23 +197,23 @@ func (check *Checker) error(at positioner, code errorCode, msg string) { check.err(check.newError(at, code, false, msg)) } -func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) { +func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) { check.error(at, code, check.sprintf(format, args...)) } -func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) { +func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) { check.err(check.newErrorf(at, code, true, format, args...)) } -func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) { +func (check *Checker) invalidAST(at positioner, format string, args ...any) { check.errorf(at, 0, "invalid AST: "+format, args...) } -func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) { +func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) { check.errorf(at, code, "invalid argument: "+format, args...) } -func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) { +func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) { check.errorf(at, code, "invalid operation: "+format, args...) } @@ -236,7 +260,7 @@ func (s atPos) Pos() token.Pos { func spanOf(at positioner) posSpan { switch x := at.(type) { case nil: - panic("internal error: nil") + panic("nil positioner") case posSpan: return x case ast.Node: @@ -259,7 +283,7 @@ func stripAnnotations(s string) string { var b strings.Builder for _, r := range s { // strip #'s and subscript digits - if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080 + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 b.WriteRune(r) } } diff --git a/libgo/go/go/types/errors_test.go b/libgo/go/go/types/errors_test.go index fdbe07c..942a9fd 100644 --- a/libgo/go/go/types/errors_test.go +++ b/libgo/go/go/types/errors_test.go @@ -15,7 +15,6 @@ func TestStripAnnotations(t *testing.T) { {"foo", "foo"}, {"foo₀", "foo"}, {"foo(T₀)", "foo(T)"}, - {"#foo(T₀)", "foo(T)"}, } { got := stripAnnotations(test.in) if got != test.want { diff --git a/libgo/go/go/types/eval.go b/libgo/go/go/types/eval.go index 5125960..c8bb005 100644 --- a/libgo/go/go/types/eval.go +++ b/libgo/go/go/types/eval.go @@ -35,9 +35,10 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type return info.Types[node], err } -// CheckExpr type checks the expression expr as if it had appeared at -// position pos of package pkg. Type information about the expression -// is recorded in info. +// CheckExpr type checks the expression expr as if it had appeared at position +// pos of package pkg. Type information about the expression is recorded in +// info. The expression may be an uninstantiated parameterized function or +// type. // // If pkg == nil, the Universe scope is used and the provided // position pos is ignored. If pkg != nil, and pos is invalid, @@ -91,8 +92,8 @@ func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr, // evaluate node var x operand - check.rawExpr(&x, expr, nil) - check.processDelayed(0) // incl. all functions + check.rawExpr(&x, expr, nil, true) // allow generic expressions + check.processDelayed(0) // incl. all functions check.recordUntyped() return nil diff --git a/libgo/go/go/types/eval_test.go b/libgo/go/go/types/eval_test.go index 41d3a61..b0745c1 100644 --- a/libgo/go/go/types/eval_test.go +++ b/libgo/go/go/types/eval_test.go @@ -111,7 +111,7 @@ func TestEvalPos(t *testing.T) { x = a + len(s) return float64(x) /* true => true, untyped bool */ - /* fmt.Println => , func(a ...interface{}) (n int, err error) */ + /* fmt.Println => , func(a ...any) (n int, err error) */ /* c => 3, untyped float */ /* T => , p.T */ /* a => , int */ @@ -195,10 +195,10 @@ func TestEvalPos(t *testing.T) { } } -// split splits string s at the first occurrence of s. +// split splits string s at the first occurrence of s, trimming spaces. func split(s, sep string) (string, string) { - i := strings.Index(s, sep) - return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):]) + before, after, _ := strings.Cut(s, sep) + return strings.TrimSpace(before), strings.TrimSpace(after) } func TestCheckExpr(t *testing.T) { @@ -218,7 +218,7 @@ type T []int type S struct{ X int } func f(a int, s string) S { - /* fmt.Println => func fmt.Println(a ...interface{}) (n int, err error) */ + /* fmt.Println => func fmt.Println(a ...any) (n int, err error) */ /* fmt.Stringer.String => func (fmt.Stringer).String() string */ fmt.Println("calling f") diff --git a/libgo/go/go/types/example_test.go b/libgo/go/go/types/example_test.go index ffbb5c0..fd52c9f 100644 --- a/libgo/go/go/types/example_test.go +++ b/libgo/go/go/types/example_test.go @@ -6,7 +6,6 @@ // access to compiled packages for import. // //go:build !arm && !arm64 -// +build !arm,!arm64 package types_test diff --git a/libgo/go/go/types/expr.go b/libgo/go/go/types/expr.go index 58962e7..44e0288 100644 --- a/libgo/go/go/types/expr.go +++ b/libgo/go/go/types/expr.go @@ -64,17 +64,17 @@ var unaryOpPredicates opPredicates func init() { // Setting unaryOpPredicates in init avoids declaration cycles. unaryOpPredicates = opPredicates{ - token.ADD: isNumeric, - token.SUB: isNumeric, - token.XOR: isInteger, - token.NOT: isBoolean, + token.ADD: allNumeric, + token.SUB: allNumeric, + token.XOR: allInteger, + token.NOT: allBoolean, } } func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool { if pred := m[op]; pred != nil { if !pred(x.typ) { - check.invalidOp(x, _UndefinedOp, "operator %s not defined for %s", op, x) + check.invalidOp(x, _UndefinedOp, "operator %s not defined on %s", op, x) return false } } else { @@ -100,8 +100,10 @@ func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) { // Typed constants must be representable in // their type after each constant operation. + // x.typ cannot be a type parameter (type + // parameters cannot be constant types). if isTyped(x.typ) { - check.representable(x, asBasic(x.typ)) + check.representable(x, under(x.typ).(*Basic)) return } @@ -114,9 +116,7 @@ func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) { } // opName returns the name of an operation, or the empty string. -// For now, only operations that might overflow are handled. -// TODO(gri) Expand this to a general mechanism giving names to -// nodes? +// Only operations that might overflow are handled. func opName(e ast.Expr) string { switch e := e.(type) { case *ast.BinaryExpr: @@ -144,6 +144,15 @@ var op2str2 = [...]string{ token.SHL: "shift", } +// If typ is a type parameter, underIs returns the result of typ.underIs(f). +// Otherwise, underIs returns the result of f(under(typ)). +func underIs(typ Type, f func(Type) bool) bool { + if tpar, _ := typ.(*TypeParam); tpar != nil { + return tpar.underIs(f) + } + return f(under(typ)) +} + // The unary expression e may be nil. It's passed in for better error messages only. func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { check.expr(x, e.X) @@ -164,19 +173,26 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { return case token.ARROW: - typ := asChan(x.typ) - if typ == nil { + u := structuralType(x.typ) + if u == nil { + check.invalidOp(x, _InvalidReceive, "cannot receive from %s: no structural type", x) + x.mode = invalid + return + } + ch, _ := u.(*Chan) + if ch == nil { check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x) x.mode = invalid return } - if typ.dir == SendOnly { + if ch.dir == SendOnly { check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x) x.mode = invalid return } + x.mode = commaok - x.typ = typ.elem + x.typ = ch.elem check.hasCallOrRecv = true return } @@ -456,8 +472,11 @@ func (check *Checker) invalidConversion(code errorCode, x *operand, target Type) // Also, if x is a constant, it must be representable as a value of typ, // and if x is the (formerly untyped) lhs operand of a non-constant // shift, it must be an integer value. -// func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { + check.updateExprType0(nil, x, typ, final) +} + +func (check *Checker) updateExprType0(parent, x ast.Expr, typ Type, final bool) { old, found := check.untyped[x] if !found { return // nothing to do @@ -499,7 +518,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { // No operands to take care of. case *ast.ParenExpr: - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) case *ast.UnaryExpr: // If x is a constant, the operands were constants. @@ -510,7 +529,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { if old.val != nil { break } - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) case *ast.BinaryExpr: if old.val != nil { @@ -522,11 +541,11 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { } else if isShift(x.Op) { // The result type depends only on lhs operand. // The rhs type was updated when checking the shift. - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) } else { // The operand types match the result type. - check.updateExprType(x.X, typ, final) - check.updateExprType(x.Y, typ, final) + check.updateExprType0(x, x.X, typ, final) + check.updateExprType0(x, x.Y, typ, final) } default: @@ -536,7 +555,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { // If the new type is not final and still untyped, just // update the recorded type. if !final && isUntyped(typ) { - old.typ = asBasic(typ) + old.typ = under(typ).(*Basic) check.untyped[x] = old return } @@ -549,8 +568,12 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { // If x is the lhs of a shift, its final type must be integer. // We already know from the shift check that it is representable // as an integer if it is a constant. - if !isInteger(typ) { - check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ) + if !allInteger(typ) { + if compilerErrorMessages { + check.invalidOp(x, _InvalidShiftOperand, "%s (shift of type %s)", parent, typ) + } else { + check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ) + } return } // Even if we have an integer, if the value is a constant we @@ -582,7 +605,11 @@ func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) { func (check *Checker) convertUntyped(x *operand, target Type) { newType, val, code := check.implicitTypeAndValue(x, target) if code != 0 { - check.invalidConversion(code, x, target.Underlying()) + t := target + if !isTypeParam(target) { + t = safeUnderlying(target) + } + check.invalidConversion(code, x, t) x.mode = invalid return } @@ -603,7 +630,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) { // If x is a constant operand, the returned constant.Value will be the // representation of x in this context. func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) { - target = expand(target) if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { return x.typ, nil, 0 } @@ -622,10 +648,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const return x.typ, nil, 0 } - switch t := optype(target).(type) { + switch u := under(target).(type) { case *Basic: if x.mode == constant_ { - v, code := check.representation(x, t) + v, code := check.representation(x, u) if code != 0 { return nil, nil, code } @@ -661,19 +687,23 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const default: return nil, nil, _InvalidUntypedConversion } - case *_Sum: - ok := t.is(func(t Type) bool { - target, _, _ := check.implicitTypeAndValue(x, t) - return target != nil - }) - if !ok { - return nil, nil, _InvalidUntypedConversion - } - // keep nil untyped (was bug #39755) - if x.isNil() { - return Typ[UntypedNil], nil, 0 - } case *Interface: + if isTypeParam(target) { + if !u.typeSet().underIs(func(u Type) bool { + if u == nil { + return false + } + t, _, _ := check.implicitTypeAndValue(x, u) + return t != nil + }) { + return nil, nil, _InvalidUntypedConversion + } + // keep nil untyped (was bug #39755) + if x.isNil() { + return Typ[UntypedNil], nil, 0 + } + break + } // Values must have concrete dynamic types. If the value is nil, // keep it untyped (this is important for tools such as go vet which // need the dynamic type for argument checking of say, print @@ -682,8 +712,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const return Typ[UntypedNil], nil, 0 } // cannot assign untyped values to non-empty interfaces - check.completeInterface(token.NoPos, t) - if !t.Empty() { + if !u.Empty() { return nil, nil, _InvalidUntypedConversion } return Default(x.typ), nil, 0 @@ -707,23 +736,33 @@ func (check *Checker) comparison(x, y *operand, op token.Token) { 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 = isOrdered(x.typ) && isOrdered(y.typ) + defined = allOrdered(x.typ) && allOrdered(y.typ) default: unreachable() } if !defined { - typ := x.typ - if x.isNil() { - typ = y.typ + 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) } - err = check.sprintf("operator %s not defined for %s", op, typ) code = _UndefinedOp } } else { @@ -765,7 +804,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { xval = constant.ToInt(x.val) } - if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int { + if allInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int { // The lhs is of integer type or an untyped constant representable // as an integer. Nothing to do. } else { @@ -802,8 +841,8 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { // Check that RHS is otherwise at least of integer type. switch { - case isInteger(y.typ): - if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { + case allInteger(y.typ): + if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y) x.mode = invalid return @@ -891,7 +930,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { } // non-constant shift - lhs must be an integer - if !isInteger(x.typ) { + if !allInteger(x.typ) { check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x) x.mode = invalid return @@ -905,19 +944,19 @@ var binaryOpPredicates opPredicates func init() { // Setting binaryOpPredicates in init avoids declaration cycles. binaryOpPredicates = opPredicates{ - token.ADD: isNumericOrString, - token.SUB: isNumeric, - token.MUL: isNumeric, - token.QUO: isNumeric, - token.REM: isInteger, - - token.AND: isInteger, - token.OR: isInteger, - token.XOR: isInteger, - token.AND_NOT: isInteger, - - token.LAND: isBoolean, - token.LOR: isBoolean, + token.ADD: allNumericOrString, + token.SUB: allNumeric, + token.MUL: allNumeric, + token.QUO: allNumeric, + token.REM: allInteger, + + token.AND: allInteger, + token.OR: allInteger, + token.XOR: allInteger, + token.AND_NOT: allInteger, + + token.LAND: allBoolean, + token.LOR: allBoolean, } } @@ -943,14 +982,35 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token return } - check.convertUntyped(x, y.typ) - if x.mode == invalid { - return + // TODO(gri) make canMix more efficient - called for each binary operation + canMix := func(x, y *operand) bool { + if IsInterface(x.typ) && !isTypeParam(x.typ) || IsInterface(y.typ) && !isTypeParam(y.typ) { + return true + } + if allBoolean(x.typ) != allBoolean(y.typ) { + return false + } + if allString(x.typ) != allString(y.typ) { + return false + } + if x.isNil() && !hasNil(y.typ) { + return false + } + if y.isNil() && !hasNil(x.typ) { + return false + } + return true } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - x.mode = invalid - return + if canMix(x, &y) { + check.convertUntyped(x, y.typ) + if x.mode == invalid { + return + } + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + x.mode = invalid + return + } } if isComparison(op) { @@ -958,7 +1018,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token return } - if !check.identical(x.typ, y.typ) { + if !Identical(x.typ, y.typ) { // only report an error if we have valid types // (otherwise we had an error reported elsewhere already) if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] { @@ -966,7 +1026,11 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token if e != nil { posn = e } - check.invalidOp(posn, _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ) + if e != nil { + check.invalidOp(posn, _MismatchedTypes, "%s (mismatched types %s and %s)", e, x.typ, y.typ) + } else { + check.invalidOp(posn, _MismatchedTypes, "%s %s= %s (mismatched types %s and %s)", lhs, op, rhs, x.typ, y.typ) + } } x.mode = invalid return @@ -979,7 +1043,7 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token if op == token.QUO || op == token.REM { // check for zero divisor - if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 { + if (x.mode == constant_ || allInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 { check.invalidOp(&y, _DivByZero, "division by zero") x.mode = invalid return @@ -1031,8 +1095,10 @@ const ( // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. +// If allowGeneric is set, the operand type may be an uninstantiated +// parameterized type or function value. // -func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { +func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind { if trace { check.trace(e.Pos(), "expr %s", e) check.indent++ @@ -1043,11 +1109,40 @@ func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { } kind := check.exprInternal(x, e, hint) + + if !allowGeneric { + check.nonGeneric(x) + } + check.record(x) return kind } +// If x is a generic function or type, nonGeneric reports an error and invalidates x.mode and x.typ. +// Otherwise it leaves x alone. +func (check *Checker) nonGeneric(x *operand) { + if x.mode == invalid || x.mode == novalue { + return + } + var what string + switch t := x.typ.(type) { + case *Named: + if isGeneric(t) { + what = "type" + } + case *Signature: + if t.tparams != nil { + what = "function" + } + } + if what != "" { + check.errorf(x.expr, _WrongTypeArgCount, "cannot use generic %s %s without instantiation", what, x.expr) + x.mode = invalid + x.typ = Typ[Invalid] + } +} + // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // @@ -1146,7 +1241,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { case hint != nil: // no composite literal type present - use hint (element type of enclosing type) typ = hint - base, _ = deref(under(typ)) // *T implies &T{} + base, _ = deref(structuralType(typ)) // *T implies &T{} default: // TODO(gri) provide better error messages depending on context @@ -1154,8 +1249,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { goto Error } - switch utyp := optype(base).(type) { + switch utyp := structuralType(base).(type) { case *Struct: + // Prevent crash if the struct referred to is not yet set up. + // See analogous comment for *Array. + if utyp.fields == nil { + check.error(e, _InvalidDeclCycle, "illegal cycle in type declaration") + goto Error + } if len(e.Elts) == 0 { break } @@ -1266,7 +1367,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration") goto Error } - visited := make(map[interface{}][]Type, len(e.Elts)) + visited := make(map[any][]Type, len(e.Elts)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { @@ -1282,9 +1383,9 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { duplicate := false // if the key is of interface type, the type is also significant when checking for duplicates xkey := keyVal(x.val) - if asInterface(utyp.key) != nil { + if IsInterface(utyp.key) { for _, vtyp := range visited[xkey] { - if check.identical(vtyp, x.typ) { + if Identical(vtyp, x.typ) { duplicate = true break } @@ -1326,16 +1427,17 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { x.typ = typ case *ast.ParenExpr: - kind := check.rawExpr(x, e.X, nil) + kind := check.rawExpr(x, e.X, nil, false) x.expr = e return kind case *ast.SelectorExpr: check.selector(x, e) - case *ast.IndexExpr: - if check.indexExpr(x, e) { - check.funcInst(x, e) + case *ast.IndexExpr, *ast.IndexListExpr: + ix := typeparams.UnpackIndexExpr(e) + if check.indexExpr(x, ix) { + check.funcInst(x, ix) } if x.mode == invalid { goto Error @@ -1352,12 +1454,16 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { if x.mode == invalid { goto Error } + // TODO(gri) we may want to permit type assertions on type parameter values at some point + if isTypeParam(x.typ) { + check.invalidOp(x, _InvalidAssert, "cannot use type assertion on type parameter value %s", x) + goto Error + } xtyp, _ := under(x.typ).(*Interface) if xtyp == nil { check.invalidOp(x, _InvalidAssert, "%s is not an interface", x) goto Error } - check.ordinaryType(x, xtyp) // x.(type) expressions are handled explicitly in type switches if e.Type == nil { // Don't use invalidAST because this can occur in the AST produced by @@ -1377,20 +1483,31 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { return check.callExpr(x, e) case *ast.StarExpr: - check.exprOrType(x, e.X) + check.exprOrType(x, e.X, false) switch x.mode { case invalid: goto Error case typexpr: x.typ = &Pointer{base: x.typ} default: - if typ := asPointer(x.typ); typ != nil { - x.mode = variable - x.typ = typ.base - } else { - check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x) + var base Type + if !underIs(x.typ, func(u Type) bool { + p, _ := u.(*Pointer) + if p == nil { + check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x) + return false + } + if base != nil && !Identical(p.base, base) { + check.invalidOp(x, _InvalidIndirection, "pointers of %s must have identical base types", x) + return false + } + base = p.base + return true + }) { goto Error } + x.mode = variable + x.typ = base } case *ast.UnaryExpr: @@ -1425,12 +1542,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // types, which are comparatively rare. default: - if typeparams.IsListExpr(e) { - // catch-all for unexpected expression lists - check.errorf(e, _Todo, "unexpected list of expressions") - } else { - panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e)) - } + panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e)) } // everything went well @@ -1443,7 +1555,7 @@ Error: return statement // avoid follow-up errors } -func keyVal(x constant.Value) interface{} { +func keyVal(x constant.Value) any { switch x.Kind() { case constant.Bool: return constant.BoolVal(x) @@ -1475,7 +1587,7 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, } var msg string if wrongType != nil { - if check.identical(method.typ, wrongType.typ) { + 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) @@ -1491,14 +1603,14 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, // If an error occurred, x.mode is set to invalid. // func (check *Checker) expr(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) + check.rawExpr(x, e, nil, false) check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr) check.singleValue(x) } // multiExpr is like expr but the result may also be a multi-value. func (check *Checker) multiExpr(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) + check.rawExpr(x, e, nil, false) check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr) } @@ -1508,16 +1620,18 @@ func (check *Checker) multiExpr(x *operand, e ast.Expr) { // func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) { assert(hint != nil) - check.rawExpr(x, e, hint) + check.rawExpr(x, e, hint, false) check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr) check.singleValue(x) } // exprOrType typechecks expression or type e and initializes x with the expression value or type. +// If allowGeneric is set, the operand type may be an uninstantiated parameterized type or function +// value. // If an error occurred, x.mode is set to invalid. // -func (check *Checker) exprOrType(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) +func (check *Checker) exprOrType(x *operand, e ast.Expr, allowGeneric bool) { + check.rawExpr(x, e, nil, allowGeneric) check.exclude(x, 1<<novalue) check.singleValue(x) } @@ -1556,7 +1670,11 @@ func (check *Checker) singleValue(x *operand) { // tuple types are never named - no need for underlying type below if t, ok := x.typ.(*Tuple); ok { assert(t.Len() != 1) - check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x) + if compilerErrorMessages { + check.errorf(x, _TooManyValues, "multiple-value %s in single-value context", x) + } else { + check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x) + } x.mode = invalid } } diff --git a/libgo/go/go/types/exprstring.go b/libgo/go/go/types/exprstring.go index f05e642..544cd84 100644 --- a/libgo/go/go/types/exprstring.go +++ b/libgo/go/go/types/exprstring.go @@ -67,16 +67,11 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { buf.WriteByte('.') buf.WriteString(x.Sel.Name) - case *ast.IndexExpr: - WriteExpr(buf, x.X) + case *ast.IndexExpr, *ast.IndexListExpr: + ix := typeparams.UnpackIndexExpr(x) + WriteExpr(buf, ix.X) buf.WriteByte('[') - exprs := typeparams.UnpackExpr(x.Index) - for i, e := range exprs { - if i > 0 { - buf.WriteString(", ") - } - WriteExpr(buf, e) - } + writeExprList(buf, ix.Indices) buf.WriteByte(']') case *ast.SliceExpr: @@ -145,29 +140,8 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { writeSigExpr(buf, x) case *ast.InterfaceType: - // separate type list types from method list - // TODO(gri) we can get rid of this extra code if writeExprList does the separation - var types []ast.Expr - var methods []*ast.Field - for _, f := range x.Methods.List { - if len(f.Names) > 1 && f.Names[0].Name == "type" { - // type list type - types = append(types, f.Type) - } else { - // method or embedded interface - methods = append(methods, f) - } - } - buf.WriteString("interface{") - writeFieldList(buf, methods, "; ", true) - if len(types) > 0 { - if len(methods) > 0 { - buf.WriteString("; ") - } - buf.WriteString("type ") - writeExprList(buf, types) - } + writeFieldList(buf, x.Methods.List, "; ", true) buf.WriteByte('}') case *ast.MapType: diff --git a/libgo/go/go/types/exprstring_test.go b/libgo/go/go/types/exprstring_test.go index 5110288..27cd532 100644 --- a/libgo/go/go/types/exprstring_test.go +++ b/libgo/go/go/types/exprstring_test.go @@ -27,6 +27,33 @@ var testExprs = []testEntry{ {"func(x int) complex128 {}", "(func(x int) complex128 literal)"}, {"[]int{1, 2, 3}", "([]int literal)"}, + // type expressions + dup("[1 << 10]byte"), + dup("[]int"), + dup("*int"), + dup("struct{x int}"), + dup("func()"), + dup("func(int, float32) string"), + dup("interface{m()}"), + dup("interface{m() string; n(x int)}"), + dup("interface{~int}"), + + dup("map[string]int"), + dup("chan E"), + dup("<-chan E"), + dup("chan<- E"), + + // new interfaces + dup("interface{int}"), + dup("interface{~int}"), + dup("interface{~int}"), + dup("interface{int | string}"), + dup("interface{~int | ~string; float64; m()}"), + + // See above. + // dup("interface{type a, b, c; ~int | ~string; float64; m()}"), + dup("interface{~T[int, string] | string}"), + // non-type expressions dup("(x)"), dup("x.f"), diff --git a/libgo/go/go/types/gotype.go b/libgo/go/go/types/gotype.go index ca1d42c..5d27bb7 100644 --- a/libgo/go/go/types/gotype.go +++ b/libgo/go/go/types/gotype.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Build this command explicitly: go build gotype.go @@ -180,7 +179,7 @@ func report(err error) { } // parse may be called concurrently -func parse(filename string, src interface{}) (*ast.File, error) { +func parse(filename string, src any) (*ast.File, error) { if *verbose { fmt.Println(filename) } diff --git a/libgo/go/go/types/hilbert_test.go b/libgo/go/go/types/hilbert_test.go index 791357a..732e269 100644 --- a/libgo/go/go/types/hilbert_test.go +++ b/libgo/go/go/types/hilbert_test.go @@ -86,7 +86,7 @@ type gen struct { bytes.Buffer } -func (g *gen) p(format string, args ...interface{}) { +func (g *gen) p(format string, args ...any) { fmt.Fprintf(&g.Buffer, format, args...) } diff --git a/libgo/go/go/types/index.go b/libgo/go/go/types/index.go index 2ba3475..db4732c 100644 --- a/libgo/go/go/types/index.go +++ b/libgo/go/go/types/index.go @@ -15,33 +15,42 @@ import ( // If e is a valid function instantiation, indexExpr returns true. // In that case x represents the uninstantiated function value and // it is the caller's responsibility to instantiate the function. -func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) { - check.exprOrType(x, e.X) +func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) { + check.exprOrType(x, e.X, true) + // x may be generic switch x.mode { case invalid: - check.use(typeparams.UnpackExpr(e.Index)...) + check.use(e.Indices...) return false case typexpr: // type instantiation x.mode = invalid - x.typ = check.varType(e) + // TODO(gri) here we re-evaluate e.X - try to avoid this + x.typ = check.varType(e.Orig) if x.typ != Typ[Invalid] { x.mode = typexpr } return false case value: - if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { + if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 { // function instantiation return true } } + // x should not be generic at this point, but be safe and check + check.nonGeneric(x) + if x.mode == invalid { + return false + } + + // ordinary index expression valid := false length := int64(-1) // valid if >= 0 - switch typ := optype(x.typ).(type) { + switch typ := under(x.typ).(type) { case *Basic: if isString(typ) { valid = true @@ -64,7 +73,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) x.typ = typ.elem case *Pointer: - if typ := asArray(typ.base); typ != nil { + if typ, _ := under(typ.base).(*Array); typ != nil { valid = true length = typ.len x.mode = variable @@ -80,7 +89,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } var key operand check.expr(&key, index) @@ -88,102 +97,101 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) // ok to continue even if indexing failed - map element type is known x.mode = mapindex x.typ = typ.elem - x.expr = e - return + x.expr = e.Orig + return false - case *_Sum: - // A sum type can be indexed if all of the sum's types - // support indexing and have the same index and element - // type. Special rules apply for maps in the sum type. - var tkey, telem Type // key is for map types only - nmaps := 0 // number of map types in sum type - if typ.is(func(t Type) bool { - var e Type - switch t := under(t).(type) { + case *Interface: + if !isTypeParam(x.typ) { + break + } + // TODO(gri) report detailed failure cause for better error messages + var key, elem Type // key != nil: we must have all maps + mode := variable // non-maps result mode + // TODO(gri) factor out closure and use it for non-typeparam cases as well + if typ.typeSet().underIs(func(u Type) bool { + l := int64(-1) // valid if >= 0 + var k, e Type // k is only set for maps + switch t := u.(type) { case *Basic: if isString(t) { e = universeByte + mode = value } case *Array: + l = t.len e = t.elem + if x.mode != variable { + mode = value + } case *Pointer: - if t := asArray(t.base); t != nil { + if t, _ := under(t.base).(*Array); t != nil { + l = t.len e = t.elem } case *Slice: e = t.elem case *Map: - // If there are multiple maps in the sum type, - // they must have identical key types. - // TODO(gri) We may be able to relax this rule - // but it becomes complicated very quickly. - if tkey != nil && !Identical(t.key, tkey) { - return false - } - tkey = t.key + k = t.key e = t.elem - nmaps++ - case *_TypeParam: - check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x) - case *instance: - panic("unimplemented") } - if e == nil || telem != nil && !Identical(e, telem) { + if e == nil { return false } - telem = e + if elem == nil { + // first type + length = l + key, elem = k, e + return true + } + // all map keys must be identical (incl. all nil) + // (that is, we cannot mix maps with other types) + if !Identical(key, k) { + return false + } + // all element types must be identical + if !Identical(elem, e) { + return false + } + // track the minimal length for arrays, if any + if l >= 0 && l < length { + length = l + } return true }) { - // If there are maps, the index expression must be assignable - // to the map key type (as for simple map index expressions). - if nmaps > 0 { + // For maps, the index expression must be assignable to the map key type. + if key != nil { index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } - var key operand - check.expr(&key, index) - check.assignment(&key, tkey, "map index") + var k operand + check.expr(&k, index) + check.assignment(&k, key, "map index") // ok to continue even if indexing failed - map element type is known - - // If there are only maps, we are done. - if nmaps == len(typ.types) { - x.mode = mapindex - x.typ = telem - x.expr = e - return - } - - // Otherwise we have mix of maps and other types. For - // now we require that the map key be an integer type. - // TODO(gri) This is probably not good enough. - valid = isInteger(tkey) - // avoid 2nd indexing error if indexing failed above - if !valid && key.mode == invalid { - x.mode = invalid - return - } - x.mode = value // map index expressions are not addressable - } else { - // no maps - valid = true - x.mode = variable + x.mode = mapindex + x.typ = elem + x.expr = e + return false } - x.typ = telem + + // no maps + valid = true + x.mode = mode + x.typ = elem } } if !valid { check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x) x.mode = invalid - return + return false } index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0) @@ -206,11 +214,20 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) { valid := false length := int64(-1) // valid if >= 0 - switch typ := optype(x.typ).(type) { + switch u := structuralString(x.typ).(type) { + case nil: + check.invalidOp(x, _NonSliceableOperand, "cannot slice %s: %s has no structural type", x, x.typ) + x.mode = invalid + return + case *Basic: - if isString(typ) { + if isString(u) { if e.Slice3 { - check.invalidOp(x, _InvalidSliceExpr, "3-index slice of string") + at := e.Max + if at == nil { + at = e // e.Index[2] should be present but be careful + } + check.invalidOp(at, _InvalidSliceExpr, "3-index slice of string") x.mode = invalid return } @@ -220,36 +237,31 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) { } // spec: "For untyped string operands the result // is a non-constant value of type string." - if typ.kind == UntypedString { + if isUntyped(x.typ) { x.typ = Typ[String] } } case *Array: valid = true - length = typ.len + length = u.len if x.mode != variable { check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x) x.mode = invalid return } - x.typ = &Slice{elem: typ.elem} + x.typ = &Slice{elem: u.elem} case *Pointer: - if typ := asArray(typ.base); typ != nil { + if u, _ := under(u.base).(*Array); u != nil { valid = true - length = typ.len - x.typ = &Slice{elem: typ.elem} + length = u.len + x.typ = &Slice{elem: u.elem} } case *Slice: valid = true // x.typ doesn't change - - case *_Sum, *_TypeParam: - check.errorf(x, 0, "generic slice expressions not yet implemented") - x.mode = invalid - return } if !valid { @@ -298,9 +310,13 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) { L: for i, x := range ind[:len(ind)-1] { if x > 0 { - for _, y := range ind[i+1:] { - if y >= 0 && x > y { - check.errorf(inNode(e, e.Rbrack), _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y) + for j, y := range ind[i+1:] { + if y >= 0 && y < x { + // The value y corresponds to the expression e.Index[i+1+j]. + // Because y >= 0, it must have been set from the expression + // when checking indices and thus e.Index[i+1+j] is not nil. + at := []ast.Expr{e.Low, e.High, e.Max}[i+1+j] + check.errorf(at, _SwappedSliceIndices, "invalid slice indices: %d < %d", y, x) break L // only report one error, ok to continue } } @@ -311,23 +327,16 @@ L: // singleIndex returns the (single) index from the index expression e. // If the index is missing, or if there are multiple indices, an error // is reported and the result is nil. -func (check *Checker) singleIndex(e *ast.IndexExpr) ast.Expr { - index := e.Index - if index == nil { - check.invalidAST(e, "missing index for %s", e) - return nil - } - - indexes := typeparams.UnpackExpr(index) - if len(indexes) == 0 { - check.invalidAST(index, "index expression %v with 0 indices", index) +func (check *Checker) singleIndex(expr *typeparams.IndexExpr) ast.Expr { + if len(expr.Indices) == 0 { + check.invalidAST(expr.Orig, "index expression %v with 0 indices", expr) return nil } - if len(indexes) > 1 { + if len(expr.Indices) > 1 { // TODO(rFindley) should this get a distinct error code? - check.invalidOp(indexes[1], _InvalidIndex, "more than one index") + check.invalidOp(expr.Indices[1], _InvalidIndex, "more than one index") } - return indexes[0] + return expr.Indices[0] } // index checks an index expression for validity. @@ -375,7 +384,7 @@ func (check *Checker) isValidIndex(x *operand, code errorCode, what string, allo } // spec: "the index x must be of integer type or an untyped constant" - if !isInteger(x.typ) { + if !allInteger(x.typ) { check.invalidArg(x, code, "%s %s must be integer", what, x) return false } diff --git a/libgo/go/go/types/infer.go b/libgo/go/go/types/infer.go index 5d49351..2678da3 100644 --- a/libgo/go/go/types/infer.go +++ b/libgo/go/go/types/infer.go @@ -8,6 +8,7 @@ package types import ( + "fmt" "go/token" "strings" ) @@ -17,17 +18,18 @@ import ( // function arguments args, if any. There must be at least one type parameter, no more type arguments // than type parameters, and params and args must match in number (incl. zero). // If successful, infer returns the complete list of type arguments, one for each type parameter. -// Otherwise the result is nil and appropriate errors will be reported unless report is set to false. +// Otherwise the result is nil and appropriate errors will be reported. // -// Inference proceeds in 3 steps: +// Inference proceeds as follows: // -// 1) Start with given type arguments. -// 2) Infer type arguments from typed function arguments. -// 3) Infer type arguments from untyped function arguments. +// Starting with given type arguments +// 1) apply FTI (function type inference) with typed arguments, +// 2) apply CTI (constraint type inference), +// 3) apply FTI with untyped function arguments, +// 4) apply CTI. // -// Constraint type inference is used after each step to expand the set of type arguments. -// -func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) { +// The process stops as soon as all type arguments are known or an error occurs. +func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) { if debug { defer func() { assert(result == nil || len(result) == len(tparams)) @@ -45,34 +47,69 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, // Function parameters and arguments must match in number. assert(params.Len() == len(args)) - // --- 0 --- // If we already have all type arguments, we're done. if len(targs) == n { return targs } // len(targs) < n - // --- 1 --- - // Explicitly provided type arguments take precedence over any inferred types; - // and types inferred via constraint type inference take precedence over types - // inferred from function arguments. - // If we have type arguments, see how far we get with constraint type inference. - if len(targs) > 0 { - var index int - targs, index = check.inferB(tparams, targs, report) - if targs == nil || index < 0 { - return targs + // If we have more than 2 arguments, we may have arguments with named and unnamed types. + // If that is the case, permutate params and args such that the arguments with named + // types are first in the list. This doesn't affect type inference if all types are taken + // as is. But when we have inexact unification enabled (as is the case for function type + // inference), when a named type is unified with an unnamed type, unification proceeds + // with the underlying type of the named type because otherwise unification would fail + // right away. This leads to an asymmetry in type inference: in cases where arguments of + // named and unnamed types are passed to parameters with identical type, different types + // (named vs underlying) may be inferred depending on the order of the arguments. + // By ensuring that named types are seen first, order dependence is avoided and unification + // succeeds where it can. + // + // This code is disabled for now pending decision whether we want to address cases like + // these and make the spec on type inference more complicated (see issue #43056). + const enableArgSorting = false + if m := len(args); m >= 2 && enableArgSorting { + // Determine indices of arguments with named and unnamed types. + var named, unnamed []int + for i, arg := range args { + if hasName(arg.typ) { + named = append(named, i) + } else { + unnamed = append(unnamed, i) + } + } + + // If we have named and unnamed types, move the arguments with + // named types first. Update the parameter list accordingly. + // Make copies so as not to clobber the incoming slices. + if len(named) != 0 && len(unnamed) != 0 { + params2 := make([]*Var, m) + args2 := make([]*operand, m) + i := 0 + for _, j := range named { + params2[i] = params.At(j) + args2[i] = args[j] + i++ + } + for _, j := range unnamed { + params2[i] = params.At(j) + args2[i] = args[j] + i++ + } + params = NewTuple(params2...) + args = args2 } } - // Continue with the type arguments we have now. Avoid matching generic + // --- 1 --- + // Continue with the type arguments we have. Avoid matching generic // parameters that already have type arguments against function arguments: // It may fail because matching uses type identity while parameter passing // uses assignment rules. Instantiate the parameter list with the type // arguments we have, and continue with that parameter list. - // First, make sure we have a "full" list of type arguments, so of which - // may be nil (unknown). + // First, make sure we have a "full" list of type arguments, some of which + // may be nil (unknown). Make a copy so as to not clobber the incoming slice. if len(targs) < n { targs2 := make([]Type, n) copy(targs2, targs) @@ -82,18 +119,17 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, // Substitute type arguments for their respective type parameters in params, // if any. Note that nil targs entries are ignored by check.subst. - // TODO(gri) Can we avoid this (we're setting known type argumemts below, + // TODO(gri) Can we avoid this (we're setting known type arguments below, // but that doesn't impact the isParameterized check for now). if params.Len() > 0 { smap := makeSubstMap(tparams, targs) - params = check.subst(token.NoPos, params, smap).(*Tuple) + params = check.subst(token.NoPos, params, smap, nil).(*Tuple) } - // --- 2 --- // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(check, false) + u := newUnifier(false) u.x.init(tparams) // Set the type arguments which we know already. @@ -104,9 +140,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, } errorf := func(kind string, tpar, targ Type, arg *operand) { - if !report { - return - } // provide a better error message if we can targs, index := u.x.types() if index == 0 { @@ -121,17 +154,21 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, } } if allFailed { - check.errorf(arg, _Todo, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams)) + check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams)) return } } smap := makeSubstMap(tparams, targs) // TODO(rFindley): pass a positioner here, rather than arg.Pos(). - inferred := check.subst(arg.Pos(), tpar, smap) + inferred := check.subst(arg.Pos(), tpar, smap, nil) + // _CannotInferTypeArgs indicates a failure of inference, though the actual + // error may be better attributed to a user-provided type argument (hence + // _InvalidTypeArg). We can't differentiate these cases, so fall back on + // the more general _CannotInferTypeArgs. if inferred != tpar { - check.errorf(arg, _Todo, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) + check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) } else { - check.errorf(arg, 0, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar) + check.errorf(arg, _CannotInferTypeArgs, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar) } } @@ -170,10 +207,11 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, return targs } + // --- 2 --- // See how far we get with constraint type inference. // Note that even if we don't have any type arguments, constraint type inference // may produce results for constraints that explicitly specify a type. - targs, index = check.inferB(tparams, targs, report) + targs, index = check.inferB(posn, tparams, targs) if targs == nil || index < 0 { return targs } @@ -189,7 +227,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, // 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 { + if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil { arg := args[i] targ := Default(arg.typ) // The default type for an untyped nil is untyped nil. We must not @@ -208,8 +246,9 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, return targs } + // --- 4 --- // Again, follow up with constraint type inference. - targs, index = check.inferB(tparams, targs, report) + targs, index = check.inferB(posn, tparams, targs) if targs == nil || index < 0 { return targs } @@ -217,24 +256,22 @@ func (check *Checker) infer(posn positioner, tparams []*TypeName, targs []Type, // At least one type argument couldn't be inferred. assert(index >= 0 && targs[index] == nil) tpar := tparams[index] - if report { - check.errorf(posn, _Todo, "cannot infer %s (%v) (%v)", tpar.name, tpar.pos, targs) - } + check.errorf(posn, _CannotInferTypeArgs, "cannot infer %s (%v)", tpar.obj.name, tpar.obj.pos) return nil } -// typeNamesString produces a string containing all the -// type names in list suitable for human consumption. -func typeNamesString(list []*TypeName) string { +// typeParamsString produces a string containing all the type parameter names +// in list suitable for human consumption. +func typeParamsString(list []*TypeParam) string { // common cases n := len(list) switch n { case 0: return "" case 1: - return list[0].name + return list[0].obj.name case 2: - return list[0].name + " and " + list[1].name + return list[0].obj.name + " and " + list[1].obj.name } // general case (n > 2) @@ -243,15 +280,15 @@ func typeNamesString(list []*TypeName) string { if i > 0 { b.WriteString(", ") } - b.WriteString(tname.name) + b.WriteString(tname.obj.name) } b.WriteString(", and ") - b.WriteString(list[n-1].name) + b.WriteString(list[n-1].obj.name) return b.String() } // IsParameterized reports whether typ contains any of the type parameters of tparams. -func isParameterized(tparams []*TypeName, typ Type) bool { +func isParameterized(tparams []*TypeParam, typ Type) bool { w := tpWalker{ seen: make(map[Type]bool), tparams: tparams, @@ -261,7 +298,7 @@ func isParameterized(tparams []*TypeName, typ Type) bool { type tpWalker struct { seen map[Type]bool - tparams []*TypeName + tparams []*TypeParam } func (w *tpWalker) isParameterized(typ Type) (res bool) { @@ -302,9 +339,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { } } - case *_Sum: - return w.isParameterizedList(t.types) - case *Signature: // t.tparams may not be nil if we are looking at a signature // of a generic function type (or an interface method) that is @@ -316,24 +350,15 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.params) || w.isParameterized(t.results) case *Interface: - if t.allMethods != nil { - // TODO(rFindley) at some point we should enforce completeness here - for _, m := range t.allMethods { - if w.isParameterized(m.typ) { - return true - } + tset := t.typeSet() + for _, m := range tset.methods { + if w.isParameterized(m.typ) { + return true } - return w.isParameterizedList(unpackType(t.allTypes)) } - - return t.iterate(func(t *Interface) bool { - for _, m := range t.methods { - if w.isParameterized(m.typ) { - return true - } - } - return w.isParameterizedList(unpackType(t.types)) - }, nil) + return tset.is(func(t *term) bool { + return t != nil && w.isParameterized(t.typ) + }) case *Map: return w.isParameterized(t.key) || w.isParameterized(t.elem) @@ -342,14 +367,11 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.elem) case *Named: - return w.isParameterizedList(t.targs) + return w.isParameterizedTypeList(t.targs.list()) - case *_TypeParam: + case *TypeParam: // t must be one of w.tparams - return t.index < len(w.tparams) && w.tparams[t.index].typ == t - - case *instance: - return w.isParameterizedList(t.targs) + return tparamIndex(w.tparams, t) >= 0 default: unreachable() @@ -358,7 +380,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return false } -func (w *tpWalker) isParameterizedList(list []Type) bool { +func (w *tpWalker) isParameterizedTypeList(list []Type) bool { for _, t := range list { if w.isParameterized(t) { return true @@ -375,12 +397,12 @@ func (w *tpWalker) isParameterizedList(list []Type) bool { // first type argument in that list that couldn't be inferred (and thus is nil). If all // type arguments were inferred successfully, index is < 0. The number of type arguments // provided may be less than the number of type parameters, but there must be at least one. -func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (types []Type, index int) { +func (check *Checker) inferB(posn positioner, tparams []*TypeParam, targs []Type) (types []Type, index int) { assert(len(tparams) >= len(targs) && len(targs) > 0) - // Setup bidirectional unification between those structural bounds + // Setup bidirectional unification between constraints // and the corresponding type arguments (which may be nil!). - u := newUnifier(check, false) + u := newUnifier(false) u.x.init(tparams) u.y = u.x // type parameters between LHS and RHS of unification are identical @@ -391,24 +413,28 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty } } - // Unify type parameters with their structural constraints, if any. + // If a constraint has a structural type, unify the corresponding type parameter with it. for _, tpar := range tparams { - typ := tpar.typ.(*_TypeParam) - sbound := check.structuralType(typ.bound) + sbound := structuralType(tpar) if sbound != nil { - if !u.unify(typ, sbound) { - if report { - check.errorf(tpar, _Todo, "%s does not match %s", tpar, sbound) - } + // If the structural type is the underlying type of a single + // defined type in the constraint, use that defined type instead. + if named, _ := tpar.singleType().(*Named); named != nil { + sbound = named + } + if !u.unify(tpar, sbound) { + // TODO(gri) improve error message by providing the type arguments + // which we know already + check.errorf(posn, _InvalidTypeArg, "%s does not match %s", tpar, sbound) return nil, 0 } } } // u.x.types() now contains the incoming type arguments plus any additional type - // arguments for which there were structural constraints. The newly inferred non- - // nil entries may still contain references to other type parameters. For instance, - // for [A any, B interface{type []C}, C interface{type *A}], if A == int + // arguments which were inferred from structural types. The newly inferred non- + // nil entries may still contain references to other type parameters. + // For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int // was given, unification produced the type list [int, []C, *A]. We eliminate the // remaining type parameters by substituting the type parameters in this type list // until nothing changes anymore. @@ -419,6 +445,34 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty } } + // The data structure of each (provided or inferred) type represents a graph, where + // each node corresponds to a type and each (directed) vertice points to a component + // type. The substitution process described above repeatedly replaces type parameter + // nodes in these graphs with the graphs of the types the type parameters stand for, + // which creates a new (possibly bigger) graph for each type. + // The substitution process will not stop if the replacement graph for a type parameter + // also contains that type parameter. + // For instance, for [A interface{ *A }], without any type argument provided for A, + // unification produces the type list [*A]. Substituting A in *A with the value for + // A will lead to infinite expansion by producing [**A], [****A], [********A], etc., + // because the graph A -> *A has a cycle through A. + // Generally, cycles may occur across multiple type parameters and inferred types + // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]). + // We eliminate cycles by walking the graphs for all type parameters. If a cycle + // through a type parameter is detected, cycleFinder nils out the respectice type + // which kills the cycle; this also means that the respective type could not be + // inferred. + // + // TODO(gri) If useful, we could report the respective cycle as an error. We don't + // do this now because type inference will fail anyway, and furthermore, + // constraints with cycles of this kind cannot currently be satisfied by + // any user-suplied type. But should that change, reporting an error + // would be wrong. + w := cycleFinder{tparams, types, make(map[Type]bool)} + for _, t := range tparams { + w.typ(t) // t != nil + } + // dirty tracks the indices of all types that may still contain type parameters. // We know that nil type entries and entries corresponding to provided (non-nil) // type arguments are clean, so exclude them from the start. @@ -437,7 +491,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty n := 0 for _, index := range dirty { t0 := types[index] - if t1 := check.subst(token.NoPos, t0, smap); t1 != t0 { + if t1 := check.subst(token.NoPos, t0, smap, nil); t1 != t0 { types[index] = t1 dirty[n] = index n++ @@ -468,15 +522,97 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty return } -// structuralType returns the structural type of a constraint, if any. -func (check *Checker) structuralType(constraint Type) Type { - if iface, _ := under(constraint).(*Interface); iface != nil { - check.completeInterface(token.NoPos, iface) - types := unpackType(iface.allTypes) - if len(types) == 1 { - return types[0] +type cycleFinder struct { + tparams []*TypeParam + types []Type + seen map[Type]bool +} + +func (w *cycleFinder) typ(typ Type) { + if w.seen[typ] { + // We have seen typ before. If it is one of the type parameters + // in tparams, iterative substitution will lead to infinite expansion. + // Nil out the corresponding type which effectively kills the cycle. + if tpar, _ := typ.(*TypeParam); tpar != nil { + if i := tparamIndex(w.tparams, tpar); i >= 0 { + // cycle through tpar + w.types[i] = nil + } + } + // If we don't have one of our type parameters, the cycle is due + // to an ordinary recursive type and we can just stop walking it. + return + } + w.seen[typ] = true + defer delete(w.seen, typ) + + switch t := typ.(type) { + case *Basic: + // nothing to do + + case *Array: + w.typ(t.elem) + + case *Slice: + w.typ(t.elem) + + case *Struct: + w.varList(t.fields) + + case *Pointer: + w.typ(t.base) + + // case *Tuple: + // This case should not occur because tuples only appear + // in signatures where they are handled explicitly. + + case *Signature: + // There are no "method types" so we should never see a recv. + assert(t.recv == nil) + if t.params != nil { + w.varList(t.params.vars) + } + if t.results != nil { + w.varList(t.results.vars) + } + + case *Union: + for _, t := range t.terms { + w.typ(t.typ) + } + + case *Interface: + for _, m := range t.methods { + w.typ(m.typ) + } + for _, t := range t.embeddeds { + w.typ(t) + } + + case *Map: + w.typ(t.key) + w.typ(t.elem) + + case *Chan: + w.typ(t.elem) + + case *Named: + for _, tpar := range t.TypeArgs().list() { + w.typ(tpar) + } + + case *TypeParam: + if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil { + w.typ(w.types[i]) } - return nil + + default: + panic(fmt.Sprintf("unexpected %T", typ)) + } +} + +func (w *cycleFinder) varList(list []*Var) { + for _, v := range list { + w.typ(v.typ) } - return constraint } diff --git a/libgo/go/go/types/initorder.go b/libgo/go/go/types/initorder.go index 77a739c..1118b58 100644 --- a/libgo/go/go/types/initorder.go +++ b/libgo/go/go/types/initorder.go @@ -7,6 +7,7 @@ package types import ( "container/heap" "fmt" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -184,6 +185,12 @@ type graphNode struct { ndeps int // number of outstanding dependencies before this object can be initialized } +// cost returns the cost of removing this node, which involves copying each +// predecessor to each successor (and vice-versa). +func (n *graphNode) cost() int { + return len(n.pred) * len(n.succ) +} + type nodeSet map[*graphNode]bool func (s *nodeSet) add(p *graphNode) { @@ -221,35 +228,48 @@ func dependencyGraph(objMap map[Object]*declInfo) []*graphNode { } } + var G, funcG []*graphNode // separate non-functions and functions + for _, n := range M { + if _, ok := n.obj.(*Func); ok { + funcG = append(funcG, n) + } else { + G = append(G, n) + } + } + // remove function nodes and collect remaining graph nodes in G // (Mutually recursive functions may introduce cycles among themselves // which are permitted. Yet such cycles may incorrectly inflate the dependency // count for variables which in turn may not get scheduled for initialization // in correct order.) - var G []*graphNode - for obj, n := range M { - if _, ok := obj.(*Func); ok { - // connect each predecessor p of n with each successor s - // and drop the function node (don't collect it in G) - for p := range n.pred { - // ignore self-cycles - if p != n { - // Each successor s of n becomes a successor of p, and - // each predecessor p of n becomes a predecessor of s. - for s := range n.succ { - // ignore self-cycles - if s != n { - p.succ.add(s) - s.pred.add(p) - delete(s.pred, n) // remove edge to n - } + // + // Note that because we recursively copy predecessors and successors + // throughout the function graph, the cost of removing a function at + // position X is proportional to cost * (len(funcG)-X). Therefore, we should + // remove high-cost functions last. + sort.Slice(funcG, func(i, j int) bool { + return funcG[i].cost() < funcG[j].cost() + }) + for _, n := range funcG { + // connect each predecessor p of n with each successor s + // and drop the function node (don't collect it in G) + for p := range n.pred { + // ignore self-cycles + if p != n { + // Each successor s of n becomes a successor of p, and + // each predecessor p of n becomes a predecessor of s. + for s := range n.succ { + // ignore self-cycles + if s != n { + p.succ.add(s) + s.pred.add(p) } - delete(p.succ, n) // remove edge to n } + delete(p.succ, n) // remove edge to n } - } else { - // collect non-function nodes - G = append(G, n) + } + for s := range n.succ { + delete(s.pred, n) // remove edge to n } } @@ -284,11 +304,11 @@ func (a nodeQueue) Less(i, j int) bool { return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order() } -func (a *nodeQueue) Push(x interface{}) { +func (a *nodeQueue) Push(x any) { panic("unreachable") } -func (a *nodeQueue) Pop() interface{} { +func (a *nodeQueue) Pop() any { n := len(*a) x := (*a)[n-1] x.index = -1 // for safety diff --git a/libgo/go/go/types/instantiate.go b/libgo/go/go/types/instantiate.go new file mode 100644 index 0000000..09a841b --- /dev/null +++ b/libgo/go/go/types/instantiate.go @@ -0,0 +1,275 @@ +// 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. + +// This file implements instantiation of generic types +// through substitution of type parameters by type arguments. + +package types + +import ( + "errors" + "fmt" + "go/token" +) + +// Instantiate instantiates the type orig with the given type arguments targs. +// orig must be a *Named or a *Signature type. If there is no error, the +// resulting Type is a new, instantiated (not parameterized) type of the same +// kind (either a *Named or a *Signature). Methods attached to a *Named type +// are also instantiated, and associated with a new *Func that has the same +// position as the original method, but nil function scope. +// +// If ctxt is non-nil, it may be used to de-duplicate the instance against +// previous instances with the same identity. As a special case, generic +// *Signature origin types are only considered identical if they are pointer +// equivalent, so that instantiating distinct (but possibly identical) +// signatures will yield different instances. +// +// If validate is set, Instantiate verifies that the number of type arguments +// and parameters match, and that the type arguments satisfy their +// corresponding type constraints. If verification fails, the resulting error +// may wrap an *ArgumentError indicating which type argument did not satisfy +// its corresponding type parameter constraint, and why. +// +// If validate is not set, Instantiate does not verify the type argument count +// or whether the type arguments satisfy their constraints. Instantiate is +// guaranteed to not return an error, but may panic. Specifically, for +// *Signature types, Instantiate will panic immediately if the type argument +// count is incorrect; for *Named types, a panic may occur later inside the +// *Named API. +func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { + if validate { + var tparams []*TypeParam + switch t := orig.(type) { + case *Named: + tparams = t.TypeParams().list() + case *Signature: + tparams = t.TypeParams().list() + } + if len(targs) != len(tparams) { + return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) + } + if i, err := (*Checker)(nil).verify(token.NoPos, tparams, targs); err != nil { + return nil, &ArgumentError{i, err} + } + } + + inst := (*Checker)(nil).instance(token.NoPos, orig, targs, ctxt) + return inst, nil +} + +// instance creates a type or function instance using the given original type +// typ and arguments targs. For Named types the resulting instance will be +// unexpanded. +func (check *Checker) instance(pos token.Pos, orig Type, targs []Type, ctxt *Context) (res Type) { + var h string + if ctxt != nil { + h = ctxt.instanceHash(orig, targs) + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + if inst := ctxt.lookup(h, orig, targs); inst != nil { + return inst + } + } + + switch orig := orig.(type) { + case *Named: + tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) + named := check.newNamed(tname, orig, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved + named.targs = newTypeList(targs) + named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { + return expandNamed(ctxt, n, pos) + } + res = named + + case *Signature: + tparams := orig.TypeParams() + if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { + return Typ[Invalid] + } + if tparams.Len() == 0 { + return orig // nothing to do (minor optimization) + } + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*Signature) + // If the signature doesn't use its type parameters, subst + // will not make a copy. In that case, make a copy now (so + // we can set tparams to nil w/o causing side-effects). + if sig == orig { + copy := *sig + sig = © + } + // After instantiating a generic signature, it is not generic + // anymore; we need to set tparams to nil. + sig.tparams = nil + res = sig + default: + // only types and functions can be generic + panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig)) + } + + if ctxt != nil { + // It's possible that we've lost a race to add named to the context. + // In this case, use whichever instance is recorded in the context. + res = ctxt.update(h, orig, targs, res) + } + + return res +} + +// validateTArgLen verifies that the length of targs and tparams matches, +// reporting an error if not. If validation fails and check is nil, +// validateTArgLen panics. +func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool { + if ntargs != ntparams { + // TODO(gri) provide better error message + if check != nil { + check.errorf(atPos(pos), _WrongTypeArgCount, "got %d arguments but %d type parameters", ntargs, ntparams) + return false + } + panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams)) + } + return true +} + +func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) { + smap := makeSubstMap(tparams, targs) + for i, tpar := range tparams { + // 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 + // the parameterized type. + bound := check.subst(pos, tpar.bound, smap, nil) + if err := check.implements(targs[i], bound); err != nil { + return i, err + } + } + return -1, nil +} + +// implements checks if V implements T and reports an error if it doesn't. +// The receiver may be nil if implements is called through an exported +// API call such as AssignableTo. +func (check *Checker) implements(V, T Type) error { + Vu := under(V) + Tu := under(T) + if Vu == Typ[Invalid] || Tu == Typ[Invalid] { + return nil // avoid follow-on errors + } + if p, _ := Vu.(*Pointer); p != nil && under(p.base) == Typ[Invalid] { + 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...)) + } + + 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) + } else { + cause = sprintf(fset, qf, false, "%s is not an interface", T) + } + return errorf("%s does not implement %s (%s)", V, T, cause) + } + + // Every type satisfies the empty interface. + if Ti.Empty() { + return nil + } + // T is not the empty interface (i.e., the type set of T is restricted) + + // An interface V with an empty type set satisfies any interface. + // (The empty set is a subset of any set.) + Vi, _ := Vu.(*Interface) + if Vi != nil && Vi.typeSet().IsEmpty() { + return nil + } + // type set of V is not empty + + // No type with non-empty type set satisfies the empty type set. + if Ti.typeSet().IsEmpty() { + return errorf("cannot implement %s (empty type set)", T) + } + + // 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 T is comparable, V must be comparable. + // Remember as a pending error and report only if we don't have a more specific error. + var pending error + if Ti.IsComparable() && !Comparable(V) { + pending = errorf("%s does not implement comparable", V) + } + + // V must also be in the set of types of T, if any. + // Constraints with empty type sets were already excluded above. + if !Ti.typeSet().hasTerms() { + return pending // nothing to do + } + + // If V is itself an interface, each of its possible types must be in the set + // of T types (i.e., the V type set must be a subset of the T type set). + // Interfaces V with empty type sets were already excluded above. + if Vi != nil { + if !Vi.typeSet().subsetOf(Ti.typeSet()) { + // TODO(gri) report which type is missing + return errorf("%s does not implement %s", V, T) + } + return pending + } + + // Otherwise, V's type must be included in the iface type set. + var alt Type + if Ti.typeSet().is(func(t *term) bool { + if !t.includes(V) { + // If V ∉ t.typ but V ∈ ~t.typ then remember this type + // so we can suggest it as an alternative in the error + // message. + if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) { + tt := *t + tt.tilde = true + if tt.includes(V) { + alt = t.typ + } + } + return true + } + return false + }) { + if alt != nil { + return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T) + } else { + return errorf("%s does not implement %s", V, T) + } + } + + return pending +} diff --git a/libgo/go/go/types/instantiate_test.go b/libgo/go/go/types/instantiate_test.go new file mode 100644 index 0000000..281c8bb --- /dev/null +++ b/libgo/go/go/types/instantiate_test.go @@ -0,0 +1,253 @@ +// 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 types_test + +import ( + "go/token" + . "go/types" + "strings" + "testing" +) + +func TestInstantiateEquality(t *testing.T) { + emptySignature := NewSignatureType(nil, nil, nil, nil, nil, false) + tests := []struct { + src string + name1 string + targs1 []Type + name2 string + targs2 []Type + wantEqual bool + }{ + { + "package basictype; type T[P any] int", + "T", []Type{Typ[Int]}, + "T", []Type{Typ[Int]}, + true, + }, + { + "package differenttypeargs; type T[P any] int", + "T", []Type{Typ[Int]}, + "T", []Type{Typ[String]}, + false, + }, + { + "package typeslice; type T[P any] int", + "T", []Type{NewSlice(Typ[Int])}, + "T", []Type{NewSlice(Typ[Int])}, + true, + }, + { + // interface{interface{...}} is equivalent to interface{...} + "package equivalentinterfaces; type T[P any] int", + "T", []Type{ + NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil), + }, + "T", []Type{ + NewInterfaceType( + nil, + []Type{ + NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil), + }, + ), + }, + true, + }, + { + // int|string is equivalent to string|int + "package equivalenttypesets; type T[P any] int", + "T", []Type{ + NewInterfaceType(nil, []Type{ + NewUnion([]*Term{NewTerm(false, Typ[Int]), NewTerm(false, Typ[String])}), + }), + }, + "T", []Type{ + NewInterfaceType(nil, []Type{ + NewUnion([]*Term{NewTerm(false, Typ[String]), NewTerm(false, Typ[Int])}), + }), + }, + true, + }, + { + "package basicfunc; func F[P any]() {}", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[Int]}, + true, + }, + { + "package funcslice; func F[P any]() {}", + "F", []Type{NewSlice(Typ[Int])}, + "F", []Type{NewSlice(Typ[Int])}, + true, + }, + { + "package funcwithparams; func F[P any](x string) float64 { return 0 }", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[Int]}, + true, + }, + { + "package differentfuncargs; func F[P any](x string) float64 { return 0 }", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[String]}, + false, + }, + { + "package funcequality; func F1[P any](x int) {}; func F2[Q any](x int) {}", + "F1", []Type{Typ[Int]}, + "F2", []Type{Typ[Int]}, + false, + }, + { + "package funcsymmetry; func F1[P any](x P) {}; func F2[Q any](x Q) {}", + "F1", []Type{Typ[Int]}, + "F2", []Type{Typ[Int]}, + false, + }, + } + + for _, test := range tests { + pkg, err := pkgForMode(".", test.src, nil, 0) + if err != nil { + t.Fatal(err) + } + + t.Run(pkg.Name(), func(t *testing.T) { + ctxt := NewContext() + + T1 := pkg.Scope().Lookup(test.name1).Type() + res1, err := Instantiate(ctxt, T1, test.targs1, false) + if err != nil { + t.Fatal(err) + } + + T2 := pkg.Scope().Lookup(test.name2).Type() + res2, err := Instantiate(ctxt, T2, test.targs2, false) + if err != nil { + t.Fatal(err) + } + + if gotEqual := res1 == res2; gotEqual != test.wantEqual { + t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual) + } + }) + } +} + +func TestInstantiateNonEquality(t *testing.T) { + const src = "package p; type T[P any] int" + + pkg1, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + pkg2, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + + // We consider T1 and T2 to be distinct types, so their instances should not + // be deduplicated by the context. + T1 := pkg1.Scope().Lookup("T").Type().(*Named) + T2 := pkg2.Scope().Lookup("T").Type().(*Named) + + ctxt := NewContext() + res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + + if res1 == res2 { + t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2) + } + if Identical(res1, res2) { + t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2) + } +} + +func TestMethodInstantiation(t *testing.T) { + const prefix = `package p + +type T[P any] struct{} + +var X T[int] + +` + tests := []struct { + decl string + want string + }{ + {"func (r T[P]) m() P", "func (T[int]).m() int"}, + {"func (r T[P]) m(P)", "func (T[int]).m(int)"}, + {"func (r *T[P]) m(P)", "func (*T[int]).m(int)"}, + {"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"}, + {"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"}, + {"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"}, + {"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"}, + } + + for _, test := range tests { + src := prefix + test.decl + pkg, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + typ := NewPointer(pkg.Scope().Lookup("X").Type()) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + m, _ := obj.(*Func) + if m == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + if got := ObjectString(m, RelativeTo(pkg)); got != test.want { + t.Errorf("instantiated %q, want %q", got, test.want) + } + } +} + +func TestImmutableSignatures(t *testing.T) { + const src = `package p + +type T[P any] struct{} + +func (T[P]) m() {} + +var _ T[int] +` + pkg, err := pkgForMode(".", src, nil, 0) + if err != nil { + t.Fatal(err) + } + typ := pkg.Scope().Lookup("T").Type().(*Named) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + if obj == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + + // Verify that the original method is not mutated by instantiating T (this + // bug manifested when subst did not return a new signature). + want := "func (T[P]).m()" + if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want { + t.Errorf("instantiated %q, want %q", got, want) + } +} + +// Copied from errors.go. +func stripAnnotations(s string) string { + var b strings.Builder + for _, r := range s { + // strip #'s and subscript digits + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 + b.WriteRune(r) + } + } + if b.Len() < len(s) { + return b.String() + } + return s +} diff --git a/libgo/go/go/types/interface.go b/libgo/go/go/types/interface.go new file mode 100644 index 0000000..b9d4660 --- /dev/null +++ b/libgo/go/go/types/interface.go @@ -0,0 +1,225 @@ +// 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 types + +import ( + "go/ast" + "go/token" +) + +// ---------------------------------------------------------------------------- +// API + +// An Interface represents an interface type. +type Interface struct { + check *Checker // for error reporting; nil once type set is computed + obj *TypeName // type name object defining this interface; or nil (for better error messages) + methods []*Func // ordered list of explicitly declared methods + embeddeds []Type // ordered list of explicitly embedded elements + embedPos *[]token.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space + implicit bool // interface is wrapper for type set literal (non-interface T, ~T, or A|B) + complete bool // indicates that obj, methods, and embeddeds are set and type set can be computed + + tset *_TypeSet // type set described by this interface, computed lazily +} + +// typeSet returns the type set for interface t. +func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, token.NoPos, t) } + +// emptyInterface represents the empty (completed) interface +var emptyInterface = Interface{complete: true, tset: &topTypeSet} + +// NewInterface returns a new interface for the given methods and embedded types. +// NewInterface takes ownership of the provided methods and may modify their types +// by setting missing receivers. +// +// Deprecated: Use NewInterfaceType instead which allows arbitrary embedded types. +func NewInterface(methods []*Func, embeddeds []*Named) *Interface { + tnames := make([]Type, len(embeddeds)) + for i, t := range embeddeds { + tnames[i] = t + } + return NewInterfaceType(methods, tnames) +} + +// NewInterfaceType returns a new interface for the given methods and embedded +// types. NewInterfaceType takes ownership of the provided methods and may +// modify their types by setting missing receivers. +// +// To avoid race conditions, the interface's type set should be computed before +// concurrent use of the interface, by explicitly calling Complete. +func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface { + if len(methods) == 0 && len(embeddeds) == 0 { + return &emptyInterface + } + + // set method receivers if necessary + typ := new(Interface) + for _, m := range methods { + if sig := m.typ.(*Signature); sig.recv == nil { + sig.recv = NewVar(m.pos, m.pkg, "", typ) + } + } + + // sort for API stability + sortMethods(methods) + + typ.methods = methods + typ.embeddeds = embeddeds + typ.complete = true + + return typ +} + +// MarkImplicit marks the interface t as implicit, meaning this interface +// corresponds to a constraint literal such as ~T or A|B without explicit +// interface embedding. MarkImplicit should be called before any concurrent use +// of implicit interfaces. +func (t *Interface) MarkImplicit() { + t.implicit = true +} + +// NumExplicitMethods returns the number of explicitly declared methods of interface t. +func (t *Interface) NumExplicitMethods() int { return len(t.methods) } + +// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). +// The methods are ordered by their unique Id. +func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } + +// NumEmbeddeds returns the number of embedded types in interface t. +func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } + +// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds(). +// The result is nil if the i'th embedded type is not a defined type. +// +// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types. +func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname } + +// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). +func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] } + +// NumMethods returns the total number of methods of interface t. +func (t *Interface) NumMethods() int { return t.typeSet().NumMethods() } + +// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). +// The methods are ordered by their unique Id. +func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) } + +// Empty reports whether t is the empty interface. +func (t *Interface) Empty() bool { return t.typeSet().IsAll() } + +// IsComparable reports whether each type in interface t's type set is comparable. +func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) } + +// IsMethodSet reports whether the interface t is fully described by its method +// set. +func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() } + +// IsImplicit reports whether the interface t is a wrapper for a type set literal. +func (t *Interface) IsImplicit() bool { return t.implicit } + +// Complete computes the interface's type set. It must be called by users of +// NewInterfaceType and NewInterface after the interface's embedded types are +// fully defined and before using the interface type in any way other than to +// form other types. The interface must not contain duplicate methods or a +// panic occurs. Complete returns the receiver. +// +// Interface types that have been completed are safe for concurrent use. +func (t *Interface) Complete() *Interface { + if !t.complete { + t.complete = true + } + t.typeSet() // checks if t.tset is already set + return t +} + +func (t *Interface) Underlying() Type { return t } +func (t *Interface) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) { + addEmbedded := func(pos token.Pos, typ Type) { + ityp.embeddeds = append(ityp.embeddeds, typ) + if ityp.embedPos == nil { + ityp.embedPos = new([]token.Pos) + } + *ityp.embedPos = append(*ityp.embedPos, pos) + } + + for _, f := range iface.Methods.List { + if len(f.Names) == 0 { + addEmbedded(f.Type.Pos(), parseUnion(check, f.Type)) + continue + } + // f.Name != nil + + // We have a method with name f.Names[0]. + name := f.Names[0] + if name.Name == "_" { + check.errorf(name, _BlankIfaceMethod, "invalid method name _") + continue // ignore + } + + typ := check.typ(f.Type) + sig, _ := typ.(*Signature) + if sig == nil { + if typ != Typ[Invalid] { + check.invalidAST(f.Type, "%s is not a method signature", typ) + } + continue // ignore + } + + // Always type-check method type parameters but complain if they are not enabled. + // (This extra check is needed here because interface method signatures don't have + // a receiver specification.) + if sig.tparams != nil { + var at positioner = f.Type + if ftyp, _ := f.Type.(*ast.FuncType); ftyp != nil && ftyp.TypeParams != nil { + at = ftyp.TypeParams + } + check.errorf(at, _InvalidMethodTypeParams, "methods cannot have type parameters") + } + + // use named receiver type if available (for better error messages) + var recvTyp Type = ityp + if def != nil { + recvTyp = def + } + sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp) + + m := NewFunc(name.Pos(), check.pkg, name.Name, sig) + check.recordDef(name, m) + ityp.methods = append(ityp.methods, m) + } + + // All methods and embedded elements for this interface are collected; + // i.e., this interface may be used in a type set computation. + ityp.complete = true + + if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 { + // empty interface + ityp.tset = &topTypeSet + return + } + + // sort for API stability + sortMethods(ityp.methods) + // (don't sort embeddeds: they must correspond to *embedPos entries) + + // Compute type set with a non-nil *Checker as soon as possible + // to report any errors. Subsequent uses of type sets will use + // this computed type set and won't need to pass in a *Checker. + // + // Pin the checker to the interface type in the interim, in case the type set + // must be used before delayed funcs are processed (see issue #48234). + // TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet + ityp.check = check + check.later(func() { + computeInterfaceTypeSet(check, iface.Pos(), ityp) + ityp.check = nil + }).describef(iface, "compute type set for %s", ityp) +} diff --git a/libgo/go/go/types/issues_test.go b/libgo/go/go/types/issues_test.go index 6d08f88..e3efb3f 100644 --- a/libgo/go/go/types/issues_test.go +++ b/libgo/go/go/types/issues_test.go @@ -636,7 +636,34 @@ var _ T = template /* ERROR cannot use.*text/template.* as T value */.Template{} } imp := importHelper{pkg: a, fallback: importer.Default()} - checkFiles(t, nil, "", []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp) - checkFiles(t, nil, "", []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp) - checkFiles(t, nil, "", []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp) + testFiles(t, nil, []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp) + testFiles(t, nil, []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp) + testFiles(t, nil, []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp) +} + +func TestIssue50646(t *testing.T) { + anyType := Universe.Lookup("any").Type() + comparableType := Universe.Lookup("comparable").Type() + + if !Comparable(anyType) { + t.Errorf("any is not a comparable type") + } + if !Comparable(comparableType) { + t.Errorf("comparable is not a comparable type") + } + + // TODO(gri) should comparable be an alias, like any? (see #50791) + if !Implements(anyType, comparableType.Underlying().(*Interface)) { + t.Errorf("any does not implement comparable") + } + if !Implements(comparableType, anyType.(*Interface)) { + t.Errorf("comparable does not implement any") + } + + if !AssignableTo(anyType, comparableType) { + t.Errorf("any not assignable to comparable") + } + if !AssignableTo(comparableType, anyType) { + t.Errorf("comparable not assignable to any") + } } diff --git a/libgo/go/go/types/labels.go b/libgo/go/go/types/labels.go index 8cf6e63..f3b7f21 100644 --- a/libgo/go/go/types/labels.go +++ b/libgo/go/go/types/labels.go @@ -36,7 +36,8 @@ func (check *Checker) labels(body *ast.BlockStmt) { } // spec: "It is illegal to define a label that is never used." - for _, obj := range all.elems { + for name, obj := range all.elems { + obj = resolve(name, obj) if lbl := obj.(*Label); !lbl.used { check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name) } diff --git a/libgo/go/go/types/lookup.go b/libgo/go/go/types/lookup.go index 9c7bfd4..8198b05 100644 --- a/libgo/go/go/types/lookup.go +++ b/libgo/go/go/types/lookup.go @@ -6,13 +6,20 @@ package types -import "go/token" +import ( + "strings" +) + +// Internal use of LookupFieldOrMethod: If the obj result is a method +// associated with a concrete (non-interface) type, the method's signature +// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing +// the method's type. // LookupFieldOrMethod looks up a field or method with given package and name // in T and returns the corresponding *Var or *Func, an index sequence, and a // bool indicating if there were any pointer indirections on the path to the // field or method. If addressable is set, T is the type of an addressable -// variable (only matters for method lookups). +// variable (only matters for method lookups). T must not be nil. // // The last index entry is the field or method index in the (possibly embedded) // type where the entry was found, either: @@ -35,29 +42,20 @@ import "go/token" // the method's formal receiver base type, nor was the receiver addressable. // func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name) -} - -// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method -// associated with a concrete (non-interface) type, the method's signature -// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing -// the method's type. -// TODO(gri) Now that we provide the *Checker, we can probably remove this -// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate. + if T == nil { + panic("LookupFieldOrMethod on nil type") + } -// lookupFieldOrMethod is like the external version but completes interfaces -// as necessary. -func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - // Methods cannot be associated to a named pointer type + // Methods cannot be associated to a named pointer type. // (spec: "The type denoted by T is called the receiver base type; // it must not be a pointer or interface type and it must be declared // in the same package as the method."). // Thus, if we have a named pointer type, proceed with the underlying // pointer type but discard the result if it is a method since we would // not have found it for T (see also issue 8590). - if t := asNamed(T); t != nil { - if p, _ := t.underlying.(*Pointer); p != nil { - obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name) + if t, _ := T.(*Named); t != nil { + if p, _ := t.Underlying().(*Pointer); p != nil { + obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name) if _, ok := obj.(*Func); ok { return nil, nil, false } @@ -65,7 +63,21 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package } } - return check.rawLookupFieldOrMethod(T, addressable, pkg, name) + obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name) + + // 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 + // in the constraint). If the structural constraint is a named pointer type (see above), we + // 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) + if _, ok := obj.(*Var); !ok { + obj, index, indirect = nil, nil, false // accept fields (variables) only + } + } + } + return } // TODO(gri) The named type consolidation and seen maps below must be @@ -73,10 +85,11 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package // types always have only one representation (even when imported // indirectly via different packages.) -// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod. -func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { +// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. +// +// 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) { // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and NewMethodSet should be kept in sync. if name == "_" { return // blank fields/methods are never found @@ -84,10 +97,11 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack typ, isPtr := deref(T) - // *typ where typ is an interface has no methods. - // Be cautious: typ may be nil (issue 39634, crash #3). - if typ == nil || isPtr && IsInterface(typ) { - return + // *typ where typ is an interface (incl. a type parameter) has no methods. + if isPtr { + if _, ok := under(typ).(*Interface); ok { + return + } } // Start with typ as single entry at shallowest depth. @@ -107,13 +121,12 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack var next []embeddedType // embedded types found at current depth // look for (pkg, name) in all types at current depth - var tpar *_TypeParam // set if obj receiver is a type parameter for _, e := range current { typ := e.typ // If we have a named type, we may have associated methods. // Look for those first. - if named := asNamed(typ); named != nil { + if named, _ := typ.(*Named); named != nil { if seen[named] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth @@ -128,7 +141,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack seen[named] = true // look for a matching attached method - if i, m := lookupMethod(named.methods, pkg, name); m != nil { + named.resolve(nil) + if i, m := named.lookupMethod(pkg, name); m != nil { // potential match // caution: method may not have a proper signature yet index = concat(e.index, i) @@ -139,19 +153,9 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack indirect = e.indirect continue // we can't have a matching field or interface method } - - // continue with underlying type, but only if it's not a type parameter - // TODO(gri) is this what we want to do for type parameters? (spec question) - // TODO(#45639) the error message produced as a result of skipping an - // underlying type parameter should be improved. - typ = named.under() - if asTypeParam(typ) != nil { - continue - } } - tpar = nil - switch t := typ.(type) { + switch t := under(typ).(type) { case *Struct: // look for a matching field and collect embedded types for i, f := range t.fields { @@ -184,29 +188,13 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack } case *Interface: - // look for a matching method - // TODO(gri) t.allMethods is sorted - use binary search - check.completeInterface(token.NoPos, t) - if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { - assert(m.typ != nil) - index = concat(e.index, i) - if obj != nil || e.multiples { - return nil, index, false // collision - } - obj = m - indirect = e.indirect - } - - case *_TypeParam: - // only consider explicit methods in the type parameter bound, not - // methods that may be common to all types in the type list. - if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil { + // look for a matching method (interface may be a type parameter) + if i, m := t.typeSet().LookupMethod(pkg, name); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { return nil, index, false // collision } - tpar = t obj = m indirect = e.indirect } @@ -221,15 +209,14 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack // is shorthand for (&x).m()". if f, _ := obj.(*Func); f != nil { // determine if method has a pointer receiver - hasPtrRecv := tpar == nil && ptrRecv(f) - if hasPtrRecv && !indirect && !addressable { + if f.hasPtrRecv() && !indirect && !addressable { return nil, nil, true // pointer/addressable receiver required } } return } - current = check.consolidateMultiples(next) + current = consolidateMultiples(next) } return nil, nil, false // not found @@ -246,7 +233,7 @@ type embeddedType struct { // consolidateMultiples collects multiple list entries with the same type // into a single entry marked as containing multiples. The result is the // consolidated list. -func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { +func consolidateMultiples(list []embeddedType) []embeddedType { if len(list) <= 1 { return list // at most one entry - nothing to do } @@ -254,7 +241,7 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { n := 0 // number of entries w/ unique type prev := make(map[Type]int) // index at which type was previously seen for _, e := range list { - if i, found := check.lookupType(prev, e.typ); found { + if i, found := lookupType(prev, e.typ); found { list[i].multiples = true // ignore this entry } else { @@ -266,14 +253,14 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { return list[:n] } -func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { +func lookupType(m map[Type]int, typ Type) (int, bool) { // fast path: maybe the types are equal if i, found := m[typ]; found { return i, true } for t, i := range m { - if check.identical(t, typ) { + if Identical(t, typ) { return i, true } } @@ -306,41 +293,34 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // 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) { - check.completeInterface(token.NoPos, T) - // fast path for common case if T.Empty() { return } - if ityp := asInterface(V); ityp != nil { - check.completeInterface(token.NoPos, ityp) - // TODO(gri) allMethods is sorted - can do this more efficiently - for _, m := range T.allMethods { - _, f := lookupMethod(ityp.allMethods, m.pkg, m.name) + if ityp, _ := under(V).(*Interface); ityp != nil { + // TODO(gri) the methods are sorted - could do this more efficiently + for _, m := range T.typeSet().methods { + _, f := ityp.typeSet().LookupMethod(m.pkg, m.name) if f == nil { - // if m is the magic method == we're ok (interfaces are comparable) - if m.name == "==" || !static { + if !static { continue } return m, f } + // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } + if ftyp.TypeParams().Len() > 0 { + panic("method with type parameters") + } - // If the methods have type parameters we don't care whether they - // are the same or not, as long as they match up. Use unification - // to see if they can be made to match. - // TODO(gri) is this always correct? what about type bounds? - // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) - if !u.unify(ftyp, mtyp) { + if !Identical(ftyp, mtyp) { return m, f } } @@ -349,17 +329,20 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, } // A concrete type implements T if it implements all methods of T. - Vd, _ := deref(V) - Vn := asNamed(Vd) - for _, m := range T.allMethods { + for _, m := range T.typeSet().methods { // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? - obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name) + obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) // Check if *V implements this method of T. if obj == nil { ptr := NewPointer(V) - obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name) + 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) } } @@ -367,10 +350,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // we must have a method (not a field of matching function type) f, _ := obj.(*Func) if f == nil { - // if m is the magic method == and V is comparable, we're ok - if m.name == "==" && Comparable(V) { - continue - } return m, nil } @@ -382,38 +361,14 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } - - // If V is a (instantiated) generic type, its methods are still - // parameterized using the original (declaration) receiver type - // parameters (subst simply copies the existing method list, it - // does not instantiate the methods). - // In order to compare the signatures, substitute the receiver - // type parameters of ftyp with V's instantiation type arguments. - // This lazily instantiates the signature of method f. - if Vn != nil && len(Vn.tparams) > 0 { - // Be careful: The number of type arguments may not match - // the number of receiver parameters. If so, an error was - // reported earlier but the length discrepancy is still - // here. Exit early in this case to prevent an assertion - // failure in makeSubstMap. - // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.rparams) != len(Vn.targs) { - return - } - ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature) + if ftyp.TypeParams().Len() > 0 { + panic("method with type parameters") } - // If the methods have type parameters we don't care whether they - // are the same or not, as long as they match up. Use unification - // to see if they can be made to match. - // TODO(gri) is this always correct? what about type bounds? - // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) - if !u.unify(ftyp, mtyp) { + if !Identical(ftyp, mtyp) { return m, f } } @@ -421,6 +376,68 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, return } +// 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 +// 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 + var mname string + if compilerErrorMessages { + mname = m.Name() + " method" + } else { + mname = "method " + m.Name() + } + if wrongType != nil { + pos := check.fset.Position(wrongType.Pos()) + if Identical(m.typ, wrongType.typ) { + if m.Name() == wrongType.Name() { + r = check.sprintf("(%s has pointer receiver) at %s", mname, pos) + } else { + r = check.sprintf("(missing %s)\n\t\thave %s^^%s at %s\n\t\twant %s^^%s", + mname, wrongType.Name(), wrongType.typ, pos, m.Name(), m.typ) + } + } else { + if compilerErrorMessages { + r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s at %s\n\t\twant %s^^%s", + mname, wrongType.Name(), wrongType.typ, pos, m.Name(), m.typ) + } else { + r = check.sprintf("(wrong type for %s)\n\thave %s at %s\nwant %s", + mname, wrongType.typ, pos, m.typ) + } + } + // 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) + ")" + } + } else if isInterfacePtr(T) { + r = "(" + check.interfacePtrError(T) + ")" + } + if r == "" { + r = check.sprintf("(missing %s)", mname) + } + return r +} + +func isInterfacePtr(T Type) bool { + p, _ := under(T).(*Pointer) + return p != nil && IsInterface(p.base) +} + +func (check *Checker) interfacePtrError(T Type) string { + assert(isInterfacePtr(T)) + if p, _ := under(T).(*Pointer); isTypeParam(p.base) { + return check.sprintf("type %s is pointer to type parameter, not type parameter", T) + } + return check.sprintf("type %s is pointer to interface, not interface", T) +} + // 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. @@ -432,7 +449,7 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun // no static check is required if T is an interface // spec: "If T is an interface type, x.(T) asserts that the // dynamic type of x implements the interface T." - if asInterface(T) != nil && !forceStrict { + if IsInterface(T) && !forceStrict { return } return check.missingMethod(T, V, false) @@ -442,6 +459,13 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun // Otherwise it returns (typ, false). func deref(typ Type) (Type, bool) { if p, _ := typ.(*Pointer); p != nil { + // p.base should never be nil, but be conservative + if p.base == nil { + if debug { + panic("pointer with nil base type (possibly due to an invalid cyclic declaration)") + } + return Typ[Invalid], true + } return p.base, true } return typ, false @@ -450,8 +474,8 @@ func deref(typ Type) (Type, bool) { // derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a // (named or unnamed) struct and returns its base. Otherwise it returns typ. func derefStructPtr(typ Type) Type { - if p := asPointer(typ); p != nil { - if asStruct(p.base) != nil { + if p, _ := under(typ).(*Pointer); p != nil { + if _, ok := under(p.base).(*Struct); ok { return p.base } } diff --git a/libgo/go/go/types/map.go b/libgo/go/go/types/map.go new file mode 100644 index 0000000..01e13b2 --- /dev/null +++ b/libgo/go/go/types/map.go @@ -0,0 +1,24 @@ +// Copyright 2011 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 types + +// A Map represents a map type. +type Map struct { + key, elem Type +} + +// NewMap returns a new map for the given key and element types. +func NewMap(key, elem Type) *Map { + return &Map{key: key, elem: elem} +} + +// Key returns the key type of map m. +func (m *Map) Key() Type { return m.key } + +// Elem returns the element type of map m. +func (m *Map) Elem() Type { return m.elem } + +func (t *Map) Underlying() Type { return t } +func (t *Map) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/methodlist.go b/libgo/go/go/types/methodlist.go new file mode 100644 index 0000000..10a2a32 --- /dev/null +++ b/libgo/go/go/types/methodlist.go @@ -0,0 +1,78 @@ +// 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 types + +import "sync" + +// methodList holds a list of methods that may be lazily resolved by a provided +// resolution method. +type methodList struct { + methods []*Func + + // guards synchronizes the instantiation of lazy methods. For lazy method + // lists, guards is non-nil and of the length passed to newLazyMethodList. + // For non-lazy method lists, guards is nil. + guards *[]sync.Once +} + +// newMethodList creates a non-lazy method list holding the given methods. +func newMethodList(methods []*Func) *methodList { + return &methodList{methods: methods} +} + +// newLazyMethodList creates a lazy method list of the given length. Methods +// may be resolved lazily for a given index by providing a resolver function. +func newLazyMethodList(length int) *methodList { + guards := make([]sync.Once, length) + return &methodList{ + methods: make([]*Func, length), + guards: &guards, + } +} + +// isLazy reports whether the receiver is a lazy method list. +func (l *methodList) isLazy() bool { + return l != nil && l.guards != nil +} + +// Add appends a method to the method list if not not already present. Add +// 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 { + 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) { + assert(!l.isLazy()) + if l == nil { + return -1, nil + } + return lookupMethod(l.methods, pkg, name) +} + +// Len returns the length of the method list. +func (l *methodList) Len() int { + if l == nil { + return 0 + } + return len(l.methods) +} + +// At returns the i'th method of the method list. At panics if i is out of +// bounds, or if the receiver is lazy and resolve is nil. +func (l *methodList) At(i int, resolve func() *Func) *Func { + if !l.isLazy() { + return l.methods[i] + } + assert(resolve != nil) + (*l.guards)[i].Do(func() { + l.methods[i] = resolve() + }) + return l.methods[i] +} diff --git a/libgo/go/go/types/methodlist_test.go b/libgo/go/go/types/methodlist_test.go new file mode 100644 index 0000000..e628bce --- /dev/null +++ b/libgo/go/go/types/methodlist_test.go @@ -0,0 +1,41 @@ +// 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 types + +import ( + "go/token" + "testing" +) + +func TestLazyMethodList(t *testing.T) { + l := newLazyMethodList(2) + + if got := l.Len(); got != 2 { + t.Fatalf("Len() = %d, want 2", got) + } + + f0 := NewFunc(token.NoPos, nil, "f0", nil) + f1 := NewFunc(token.NoPos, nil, "f1", nil) + + // Verify that methodList.At is idempotent, by calling it repeatedly with a + // resolve func that returns different pointer values (f0 or f1). + steps := []struct { + index int + resolve *Func // the *Func returned by the resolver + want *Func // the actual *Func returned by methodList.At + }{ + {0, f0, f0}, + {0, f1, f0}, + {1, f1, f1}, + {1, f0, f1}, + } + + for i, step := range steps { + got := l.At(step.index, func() *Func { return step.resolve }) + if got != step.want { + t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name()) + } + } +} diff --git a/libgo/go/go/types/methodset.go b/libgo/go/go/types/methodset.go index ae8011a..c1d1e93 100644 --- a/libgo/go/go/types/methodset.go +++ b/libgo/go/go/types/methodset.go @@ -111,7 +111,7 @@ func NewMethodSet(T Type) *MethodSet { // If we have a named type, we may have associated methods. // Look for those first. - if named := asNamed(typ); named != nil { + if named, _ := typ.(*Named); named != nil { if seen[named] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth @@ -125,17 +125,12 @@ func NewMethodSet(T Type) *MethodSet { } seen[named] = true - mset = mset.add(named.methods, e.index, e.indirect, e.multiples) - - // continue with underlying type, but only if it's not a type parameter - // TODO(rFindley): should this use named.under()? Can there be a difference? - typ = named.underlying - if _, ok := typ.(*_TypeParam); ok { - continue + for i := 0; i < named.NumMethods(); i++ { + mset = mset.addOne(named.Method(i), concat(e.index, i), e.indirect, e.multiples) } } - switch t := typ.(type) { + switch t := under(typ).(type) { case *Struct: for i, f := range t.fields { if fset == nil { @@ -157,10 +152,7 @@ func NewMethodSet(T Type) *MethodSet { } case *Interface: - mset = mset.add(t.allMethods, e.index, true, e.multiples) - - case *_TypeParam: - mset = mset.add(t.Bound().allMethods, e.index, true, e.multiples) + mset = mset.add(t.typeSet().methods, e.index, true, e.multiples) } } @@ -190,12 +182,7 @@ func NewMethodSet(T Type) *MethodSet { } } - // It's ok to call consolidateMultiples with a nil *Checker because - // MethodSets are not used internally (outside debug mode). When used - // externally, interfaces are expected to be completed and then we do - // not need a *Checker to complete them when (indirectly) calling - // Checker.identical via consolidateMultiples. - current = (*Checker)(nil).consolidateMultiples(next) + current = consolidateMultiples(next) } if len(base) == 0 { @@ -229,42 +216,28 @@ func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) if len(list) == 0 { return s } - if s == nil { - s = make(methodSet) - } for i, f := range list { - key := f.Id() - // if f is not in the set, add it - if !multiples { - // TODO(gri) A found method may not be added because it's not in the method set - // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method - // set and may not collide with the first one, thus leading to a false positive. - // Is that possible? Investigate. - if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { - s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} - continue - } - } - s[key] = nil // collision + s = s.addOne(f, concat(index, i), indirect, multiples) } return s } -// ptrRecv reports whether the receiver is of the form *T. -func ptrRecv(f *Func) bool { - // If a method's receiver type is set, use that as the source of truth for the receiver. - // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty - // signature. We may reach here before the signature is fully set up: we must explicitly - // check if the receiver is set (we cannot just look for non-nil f.typ). - if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil { - _, isPtr := deref(sig.recv.typ) - return isPtr +func (s methodSet) addOne(f *Func, index []int, indirect bool, multiples bool) methodSet { + if s == nil { + s = make(methodSet) } - - // If a method's type is not set it may be a method/function that is: - // 1) client-supplied (via NewFunc with no signature), or - // 2) internally created but not yet type-checked. - // For case 1) we can't do anything; the client must know what they are doing. - // For case 2) we can use the information gathered by the resolver. - return f.hasPtrRecv + key := f.Id() + // if f is not in the set, add it + if !multiples { + // TODO(gri) A found method may not be added because it's not in the method set + // (!indirect && f.hasPtrRecv()). A 2nd method on the same level may be in the method + // set and may not collide with the first one, thus leading to a false positive. + // Is that possible? Investigate. + if _, found := s[key]; !found && (indirect || !f.hasPtrRecv()) { + s[key] = &Selection{MethodVal, nil, f, index, indirect} + return s + } + } + s[key] = nil // collision + return s } diff --git a/libgo/go/go/types/methodset_test.go b/libgo/go/go/types/methodset_test.go index 4a373fa..73a8442 100644 --- a/libgo/go/go/types/methodset_test.go +++ b/libgo/go/go/types/methodset_test.go @@ -7,7 +7,6 @@ package types_test import ( "testing" - "go/internal/typeparams" . "go/types" ) @@ -47,12 +46,15 @@ func TestNewMethodSet(t *testing.T) { genericTests := map[string][]method{ // By convention, look up a in the scope of "g" - "type C interface{ f() }; func g[T C](a T){}": {{"f", []int{0}, true}}, - "type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}}, - "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}}, + "type C interface{ f() }; func g[T C](a T){}": {{"f", []int{0}, true}}, + "type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}}, - // Issue #45639. - "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {}, + // Issue #43621: We don't allow this anymore. Keep this code in case we + // decide to revisit this decision. + // "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}}, + + // Issue #45639: We also don't allow this anymore. + // "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {}, } check := func(src string, methods []method, generic bool) { @@ -101,9 +103,7 @@ func TestNewMethodSet(t *testing.T) { check(src, methods, false) } - if typeparams.Enabled { - for src, methods := range genericTests { - check(src, methods, true) - } + for src, methods := range genericTests { + check(src, methods, true) } } diff --git a/libgo/go/go/types/mono.go b/libgo/go/go/types/mono.go new file mode 100644 index 0000000..84e1e97 --- /dev/null +++ b/libgo/go/go/types/mono.go @@ -0,0 +1,336 @@ +// 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 types + +import ( + "go/ast" + "go/token" +) + +// This file implements a check to validate that a Go package doesn't +// have unbounded recursive instantiation, which is not compatible +// with compilers using static instantiation (such as +// monomorphization). +// +// It implements a sort of "type flow" analysis by detecting which +// type parameters are instantiated with other type parameters (or +// types derived thereof). A package cannot be statically instantiated +// if the graph has any cycles involving at least one derived type. +// +// Concretely, we construct a directed, weighted graph. Vertices are +// used to represent type parameters as well as some defined +// types. Edges are used to represent how types depend on each other: +// +// * Everywhere a type-parameterized function or type is instantiated, +// we add edges to each type parameter from the vertices (if any) +// representing each type parameter or defined type referenced by +// the type argument. If the type argument is just the referenced +// type itself, then the edge has weight 0, otherwise 1. +// +// * For every defined type declared within a type-parameterized +// function or method, we add an edge of weight 1 to the defined +// type from each ambient type parameter. +// +// For example, given: +// +// func f[A, B any]() { +// type T int +// f[T, map[A]B]() +// } +// +// we construct vertices representing types A, B, and T. Because of +// declaration "type T int", we construct edges T<-A and T<-B with +// weight 1; and because of instantiation "f[T, map[A]B]" we construct +// edges A<-T with weight 0, and B<-A and B<-B with weight 1. +// +// Finally, we look for any positive-weight cycles. Zero-weight cycles +// are allowed because static instantiation will reach a fixed point. + +type monoGraph struct { + vertices []monoVertex + edges []monoEdge + + // canon maps method receiver type parameters to their respective + // receiver type's type parameters. + canon map[*TypeParam]*TypeParam + + // nameIdx maps a defined type or (canonical) type parameter to its + // vertex index. + nameIdx map[*TypeName]int +} + +type monoVertex struct { + weight int // weight of heaviest known path to this vertex + pre int // previous edge (if any) in the above path + len int // length of the above path + + // obj is the defined type or type parameter represented by this + // vertex. + obj *TypeName +} + +type monoEdge struct { + dst, src int + weight int + + pos token.Pos + typ Type +} + +func (check *Checker) monomorph() { + // We detect unbounded instantiation cycles using a variant of + // Bellman-Ford's algorithm. Namely, instead of always running |V| + // iterations, we run until we either reach a fixed point or we've + // found a path of length |V|. This allows us to terminate earlier + // when there are no cycles, which should be the common case. + + again := true + for again { + again = false + + for i, edge := range check.mono.edges { + src := &check.mono.vertices[edge.src] + dst := &check.mono.vertices[edge.dst] + + // N.B., we're looking for the greatest weight paths, unlike + // typical Bellman-Ford. + w := src.weight + edge.weight + if w <= dst.weight { + continue + } + + dst.pre = i + dst.len = src.len + 1 + if dst.len == len(check.mono.vertices) { + check.reportInstanceLoop(edge.dst) + return + } + + dst.weight = w + again = true + } + } +} + +func (check *Checker) reportInstanceLoop(v int) { + var stack []int + seen := make([]bool, len(check.mono.vertices)) + + // We have a path that contains a cycle and ends at v, but v may + // only be reachable from the cycle, not on the cycle itself. We + // start by walking backwards along the path until we find a vertex + // that appears twice. + for !seen[v] { + stack = append(stack, v) + seen[v] = true + v = check.mono.edges[check.mono.vertices[v].pre].src + } + + // Trim any vertices we visited before visiting v the first + // time. Since v is the first vertex we found within the cycle, any + // vertices we visited earlier cannot be part of the cycle. + for stack[0] != v { + stack = stack[1:] + } + + // TODO(mdempsky): Pivot stack so we report the cycle from the top? + + obj0 := check.mono.vertices[v].obj + check.errorf(obj0, _InvalidInstanceCycle, "instantiation cycle:") + + qf := RelativeTo(check.pkg) + for _, v := range stack { + edge := check.mono.edges[check.mono.vertices[v].pre] + obj := check.mono.vertices[edge.dst].obj + + switch obj.Type().(type) { + default: + panic("unexpected type") + case *Named: + check.errorf(atPos(edge.pos), _InvalidInstanceCycle, "\t%s implicitly parameterized by %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented + case *TypeParam: + check.errorf(atPos(edge.pos), _InvalidInstanceCycle, "\t%s instantiated as %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented + } + } +} + +// recordCanon records that tpar is the canonical type parameter +// corresponding to method type parameter mpar. +func (w *monoGraph) recordCanon(mpar, tpar *TypeParam) { + if w.canon == nil { + w.canon = make(map[*TypeParam]*TypeParam) + } + w.canon[mpar] = tpar +} + +// recordInstance records that the given type parameters were +// instantiated with the corresponding type arguments. +func (w *monoGraph) recordInstance(pkg *Package, pos token.Pos, tparams []*TypeParam, targs []Type, xlist []ast.Expr) { + for i, tpar := range tparams { + pos := pos + if i < len(xlist) { + pos = xlist[i].Pos() + } + w.assign(pkg, pos, tpar, targs[i]) + } +} + +// assign records that tpar was instantiated as targ at pos. +func (w *monoGraph) assign(pkg *Package, pos token.Pos, tpar *TypeParam, targ Type) { + // Go generics do not have an analog to C++`s template-templates, + // where a template parameter can itself be an instantiable + // template. So any instantiation cycles must occur within a single + // package. Accordingly, we can ignore instantiations of imported + // type parameters. + // + // TODO(mdempsky): Push this check up into recordInstance? All type + // parameters in a list will appear in the same package. + if tpar.Obj().Pkg() != pkg { + return + } + + // flow adds an edge from vertex src representing that typ flows to tpar. + flow := func(src int, typ Type) { + weight := 1 + if typ == targ { + weight = 0 + } + + w.addEdge(w.typeParamVertex(tpar), src, weight, pos, targ) + } + + // Recursively walk the type argument to find any defined types or + // type parameters. + var do func(typ Type) + do = func(typ Type) { + switch typ := typ.(type) { + default: + panic("unexpected type") + + case *TypeParam: + assert(typ.Obj().Pkg() == pkg) + flow(w.typeParamVertex(typ), typ) + + case *Named: + if src := w.localNamedVertex(pkg, typ.Origin()); src >= 0 { + flow(src, typ) + } + + targs := typ.TypeArgs() + for i := 0; i < targs.Len(); i++ { + do(targs.At(i)) + } + + case *Array: + do(typ.Elem()) + case *Basic: + // ok + case *Chan: + do(typ.Elem()) + case *Map: + do(typ.Key()) + do(typ.Elem()) + case *Pointer: + do(typ.Elem()) + case *Slice: + do(typ.Elem()) + + case *Interface: + for i := 0; i < typ.NumMethods(); i++ { + do(typ.Method(i).Type()) + } + case *Signature: + tuple := func(tup *Tuple) { + for i := 0; i < tup.Len(); i++ { + do(tup.At(i).Type()) + } + } + tuple(typ.Params()) + tuple(typ.Results()) + case *Struct: + for i := 0; i < typ.NumFields(); i++ { + do(typ.Field(i).Type()) + } + } + } + do(targ) +} + +// localNamedVertex returns the index of the vertex representing +// named, or -1 if named doesn't need representation. +func (w *monoGraph) localNamedVertex(pkg *Package, named *Named) int { + obj := named.Obj() + if obj.Pkg() != pkg { + return -1 // imported type + } + + root := pkg.Scope() + if obj.Parent() == root { + return -1 // package scope, no ambient type parameters + } + + if idx, ok := w.nameIdx[obj]; ok { + return idx + } + + idx := -1 + + // Walk the type definition's scope to find any ambient type + // parameters that it's implicitly parameterized by. + for scope := obj.Parent(); scope != root; scope = scope.Parent() { + for _, elem := range scope.elems { + if elem, ok := elem.(*TypeName); ok && !elem.IsAlias() && elem.Pos() < obj.Pos() { + if tpar, ok := elem.Type().(*TypeParam); ok { + if idx < 0 { + idx = len(w.vertices) + w.vertices = append(w.vertices, monoVertex{obj: obj}) + } + + w.addEdge(idx, w.typeParamVertex(tpar), 1, obj.Pos(), tpar) + } + } + } + } + + if w.nameIdx == nil { + w.nameIdx = make(map[*TypeName]int) + } + w.nameIdx[obj] = idx + return idx +} + +// typeParamVertex returns the index of the vertex representing tpar. +func (w *monoGraph) typeParamVertex(tpar *TypeParam) int { + if x, ok := w.canon[tpar]; ok { + tpar = x + } + + obj := tpar.Obj() + + if idx, ok := w.nameIdx[obj]; ok { + return idx + } + + if w.nameIdx == nil { + w.nameIdx = make(map[*TypeName]int) + } + + idx := len(w.vertices) + w.vertices = append(w.vertices, monoVertex{obj: obj}) + w.nameIdx[obj] = idx + return idx +} + +func (w *monoGraph) addEdge(dst, src, weight int, pos token.Pos, typ Type) { + // TODO(mdempsky): Deduplicate redundant edges? + w.edges = append(w.edges, monoEdge{ + dst: dst, + src: src, + weight: weight, + + pos: pos, + typ: typ, + }) +} diff --git a/libgo/go/go/types/mono_test.go b/libgo/go/go/types/mono_test.go new file mode 100644 index 0000000..f9e72c3 --- /dev/null +++ b/libgo/go/go/types/mono_test.go @@ -0,0 +1,97 @@ +// 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 types_test + +import ( + "bytes" + "errors" + "fmt" + "go/ast" + "go/importer" + "go/parser" + "go/token" + "go/types" + "runtime" + "strings" + "testing" +) + +func checkMono(t *testing.T, body string) error { + if runtime.Compiler == "gccgo" { + t.Skip("skipping for gofronted: fails to import unsafe") + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "x.go", "package x; import `unsafe`; var _ unsafe.Pointer;\n"+body, 0) + if err != nil { + t.Fatal(err) + } + files := []*ast.File{file} + + var buf bytes.Buffer + conf := types.Config{ + Error: func(err error) { fmt.Fprintln(&buf, err) }, + Importer: importer.Default(), + } + conf.Check("x", fset, files, nil) + if buf.Len() == 0 { + return nil + } + return errors.New(strings.TrimRight(buf.String(), "\n")) +} + +func TestMonoGood(t *testing.T) { + for i, good := range goods { + if err := checkMono(t, good); err != nil { + t.Errorf("%d: unexpected failure: %v", i, err) + } + } +} + +func TestMonoBad(t *testing.T) { + for i, bad := range bads { + if err := checkMono(t, bad); err == nil { + t.Errorf("%d: unexpected success", i) + } else { + t.Log(err) + } + } +} + +var goods = []string{ + "func F[T any](x T) { F(x) }", + "func F[T, U, V any]() { F[U, V, T](); F[V, T, U]() }", + "type Ring[A, B, C any] struct { L *Ring[B, C, A]; R *Ring[C, A, B] }", + "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte }", + "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte; var _ U[int] }", + "type U[T any] [unsafe.Sizeof(F[*T])]byte; func F[T any]() { var _ U[U[int]] }", + "func F[T any]() { type A = int; F[A]() }", +} + +// TODO(mdempsky): Validate specific error messages and positioning. + +var bads = []string{ + "func F[T any](x T) { F(&x) }", + "func F[T any]() { F[*T]() }", + "func F[T any]() { F[[]T]() }", + "func F[T any]() { F[[1]T]() }", + "func F[T any]() { F[chan T]() }", + "func F[T any]() { F[map[*T]int]() }", + "func F[T any]() { F[map[error]T]() }", + "func F[T any]() { F[func(T)]() }", + "func F[T any]() { F[func() T]() }", + "func F[T any]() { F[struct{ t T }]() }", + "func F[T any]() { F[interface{ t() T }]() }", + "type U[_ any] int; func F[T any]() { F[U[T]]() }", + "func F[T any]() { type U int; F[U]() }", + "func F[T any]() { type U int; F[*U]() }", + "type U[T any] int; func (U[T]) m() { var _ U[*T] }", + "type U[T any] int; func (*U[T]) m() { var _ U[*T] }", + "type U[T1 any] [unsafe.Sizeof(F[*T1])]byte; func F[T2 any]() { var _ U[T2] }", + "func F[A, B, C, D, E any]() { F[B, C, D, E, *A]() }", + "type U[_ any] int; const X = unsafe.Sizeof(func() { type A[T any] U[A[*T]] })", + "func F[T any]() { type A = *T; F[A]() }", + "type A[T any] struct { _ A[*T] }", +} diff --git a/libgo/go/go/types/named.go b/libgo/go/go/types/named.go new file mode 100644 index 0000000..28db260 --- /dev/null +++ b/libgo/go/go/types/named.go @@ -0,0 +1,391 @@ +// Copyright 2011 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 types + +import ( + "go/token" + "sync" +) + +// A Named represents a named (defined) type. +type Named struct { + check *Checker + obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types + orig *Named // original, uninstantiated type + fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting) + underlying Type // possibly a *Named during setup; never a *Named once set up completely + tparams *TypeParamList // type parameters, or nil + targs *TypeList // type arguments (after instantiation), or nil + + // methods declared for this type (not the method set of this type). + // Signatures are type-checked lazily. + // For non-instantiated types, this is a fully populated list of methods. For + // instantiated types, this is a 'lazy' list, and methods are instantiated + // when they are first accessed. + methods *methodList + + // resolver may be provided to lazily resolve type parameters, underlying, and methods. + resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList) + once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing +} + +// NewNamed returns a new named type for the given type name, underlying type, and associated methods. +// If the given type name obj doesn't have a type yet, its type is set to the returned named type. +// The underlying type must not be a *Named. +func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { + if _, ok := underlying.(*Named); ok { + panic("underlying type must not be *Named") + } + return (*Checker)(nil).newNamed(obj, nil, underlying, nil, newMethodList(methods)) +} + +func (t *Named) resolve(ctxt *Context) *Named { + if t.resolver == nil { + return t + } + + t.once.Do(func() { + // TODO(mdempsky): Since we're passing t to the resolver anyway + // (necessary because types2 expects the receiver type for methods + // on defined interface types to be the Named rather than the + // underlying Interface), maybe it should just handle calling + // SetTypeParams, SetUnderlying, and AddMethod instead? Those + // methods would need to support reentrant calls though. It would + // also make the API more future-proof towards further extensions + // (like SetTypeParams). + t.tparams, t.underlying, t.methods = t.resolver(ctxt, t) + t.fromRHS = t.underlying // for cycle detection + }) + return t +} + +// newNamed is like NewNamed but with a *Checker receiver and additional orig argument. +func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods *methodList) *Named { + typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods} + if typ.orig == nil { + typ.orig = typ + } + if obj.typ == nil { + obj.typ = typ + } + // Ensure that typ is always expanded and sanity-checked. + if check != nil { + check.defTypes = append(check.defTypes, typ) + } + return typ +} + +// Obj returns the type name for the declaration defining the named type t. For +// instantiated types, this is the type name of the base type. +func (t *Named) Obj() *TypeName { + return t.orig.obj // for non-instances this is the same as t.obj +} + +// Origin returns the parameterized type from which the named type t is +// instantiated. If t is not an instantiated type, the result is t. +func (t *Named) Origin() *Named { return t.orig } + +// TODO(gri) Come up with a better representation and API to distinguish +// between parameterized instantiated and non-instantiated types. + +// TypeParams returns the type parameters of the named type t, or nil. +// The result is non-nil for an (originally) parameterized type even if it is instantiated. +func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } + +// SetTypeParams sets the type parameters of the named type t. +// t must not have type arguments. +func (t *Named) SetTypeParams(tparams []*TypeParam) { + assert(t.targs.Len() == 0) + t.resolve(nil).tparams = bindTParams(tparams) +} + +// TypeArgs returns the type arguments used to instantiate the named type t. +func (t *Named) TypeArgs() *TypeList { return t.targs } + +// NumMethods returns the number of explicit methods whose receiver is named type t. +func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() } + +// Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). +func (t *Named) Method(i int) *Func { + t.resolve(nil) + return t.methods.At(i, func() *Func { + return t.instantiateMethod(i) + }) +} + +// instiateMethod instantiates the i'th method for an instantiated receiver. +func (t *Named) instantiateMethod(i int) *Func { + assert(t.TypeArgs().Len() > 0) // t must be an instance + + // t.orig.methods is not lazy. origm is the method instantiated with its + // receiver type parameters (the "origin" method). + origm := t.orig.Method(i) + assert(origm != nil) + + check := t.check + // Ensure that the original method is type-checked. + if check != nil { + check.objDecl(origm, nil) + } + + origSig := origm.typ.(*Signature) + rbase, _ := deref(origSig.Recv().Type()) + + // If rbase is t, then origm is already the instantiated method we're looking + // for. In this case, we return origm to preserve the invariant that + // traversing Method->Receiver Type->Method should get back to the same + // method. + // + // This occurs if t is instantiated with the receiver type parameters, as in + // the use of m in func (r T[_]) m() { r.m() }. + if rbase == t { + return origm + } + + sig := origSig + // We can only substitute if we have a correspondence between type arguments + // and type parameters. This check is necessary in the presence of invalid + // code. + if origSig.RecvTypeParams().Len() == t.targs.Len() { + ctxt := check.bestContext(nil) + smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list()) + sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature) + } + + if sig == origSig { + // No substitution occurred, but we still need to create a new signature to + // hold the instantiated receiver. + copy := *origSig + sig = © + } + + var rtyp Type + if origm.hasPtrRecv() { + rtyp = NewPointer(t) + } else { + rtyp = t + } + + sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) + return NewFunc(origm.pos, origm.pkg, origm.name, sig) +} + +// SetUnderlying sets the underlying type and marks t as complete. +// t must not have type arguments. +func (t *Named) SetUnderlying(underlying Type) { + assert(t.targs.Len() == 0) + if underlying == nil { + panic("underlying type must not be nil") + } + if _, ok := underlying.(*Named); ok { + panic("underlying type must not be *Named") + } + t.resolve(nil).underlying = underlying + if t.fromRHS == nil { + t.fromRHS = underlying // for cycle detection + } +} + +// AddMethod adds method m unless it is already in the method list. +// t must not have type arguments. +func (t *Named) AddMethod(m *Func) { + assert(t.targs.Len() == 0) + t.resolve(nil) + if t.methods == nil { + t.methods = newMethodList(nil) + } + t.methods.Add(m) +} + +func (t *Named) Underlying() Type { return t.resolve(nil).underlying } +func (t *Named) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +// under returns the expanded underlying type of n0; possibly by following +// forward chains of named types. If an underlying type is found, resolve +// the chain by setting the underlying type for each defined type in the +// chain before returning it. If no underlying type is found or a cycle +// is detected, the result is Typ[Invalid]. If a cycle is detected and +// n0.check != nil, the cycle is reported. +// +// This is necessary because the underlying type of named may be itself a +// named type that is incomplete: +// +// type ( +// A B +// B *C +// C A +// ) +// +// The type of C is the (named) type of A which is incomplete, +// and which has as its underlying type the named type B. +func (n0 *Named) under() Type { + u := n0.Underlying() + + // If the underlying type of a defined type is not a defined + // (incl. instance) type, then that is the desired underlying + // type. + var n1 *Named + switch u1 := u.(type) { + case nil: + // After expansion via Underlying(), we should never encounter a nil + // underlying. + panic("nil underlying") + default: + // common case + return u + case *Named: + // handled below + n1 = u1 + } + + if n0.check == nil { + panic("Named.check == nil but type is incomplete") + } + + // Invariant: after this point n0 as well as any named types in its + // underlying chain should be set up when this function exits. + check := n0.check + n := n0 + + seen := make(map[*Named]int) // types that need their underlying resolved + var path []Object // objects encountered, for cycle reporting + +loop: + for { + seen[n] = len(seen) + path = append(path, n.obj) + n = n1 + if i, ok := seen[n]; ok { + // cycle + check.cycleError(path[i:]) + u = Typ[Invalid] + break + } + u = n.Underlying() + switch u1 := u.(type) { + case nil: + u = Typ[Invalid] + break loop + default: + break loop + case *Named: + // Continue collecting *Named types in the chain. + n1 = u1 + } + } + + for n := range seen { + // We should never have to update the underlying type of an imported type; + // those underlying types should have been resolved during the import. + // Also, doing so would lead to a race condition (was issue #31749). + // Do this check always, not just in debug mode (it's cheap). + if n.obj.pkg != check.pkg { + panic("imported type with unresolved underlying type") + } + n.underlying = u + } + + return u +} + +func (n *Named) setUnderlying(typ Type) { + if n != nil { + n.underlying = typ + } +} + +func (n *Named) lookupMethod(pkg *Package, name string) (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) + if i < 0 { + return -1, nil + } + // For instances, m.Method(i) will be different from the orig method. + return i, n.Method(i) +} + +// bestContext returns the best available context. In order of preference: +// - the given ctxt, if non-nil +// - check.ctxt, if check is non-nil +// - a new Context +func (check *Checker) bestContext(ctxt *Context) *Context { + if ctxt != nil { + return ctxt + } + if check != nil { + if check.ctxt == nil { + check.ctxt = NewContext() + } + return check.ctxt + } + return NewContext() +} + +// expandNamed ensures that the underlying type of n is instantiated. +// The underlying type will be Typ[Invalid] if there was an error. +func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods *methodList) { + n.orig.resolve(ctxt) + assert(n.orig.underlying != nil) + + check := n.check + + if _, unexpanded := n.orig.underlying.(*Named); unexpanded { + // We should only get an unexpanded underlying here during type checking + // (for example, in recursive type declarations). + assert(check != nil) + } + + // Mismatching arg and tparam length may be checked elsewhere. + if n.orig.tparams.Len() == n.targs.Len() { + // We must always have a context, to avoid infinite recursion. + ctxt = check.bestContext(ctxt) + h := ctxt.instanceHash(n.orig, n.targs.list()) + // ensure that an instance is recorded for h to avoid infinite recursion. + ctxt.update(h, n.orig, n.TypeArgs().list(), n) + + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) + underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt) + // If the underlying of n is an interface, we need to set the receiver of + // its methods accurately -- we set the receiver of interface methods on + // the RHS of a type declaration to the defined type. + if iface, _ := underlying.(*Interface); iface != nil { + if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied { + // If the underlying doesn't actually use type parameters, it's possible + // that it wasn't substituted. In this case we need to create a new + // *Interface before modifying receivers. + if iface == n.orig.underlying { + iface = &Interface{ + embeddeds: iface.embeddeds, + complete: iface.complete, + implicit: iface.implicit, // should be false but be conservative + } + underlying = iface + } + iface.methods = methods + } + } + } else { + underlying = Typ[Invalid] + } + + return n.orig.tparams, underlying, newLazyMethodList(n.orig.methods.Len()) +} + +// safeUnderlying returns the underlying of typ without expanding instances, to +// avoid infinite recursion. +// +// TODO(rfindley): eliminate this function or give it a better name. +func safeUnderlying(typ Type) Type { + if t, _ := typ.(*Named); t != nil { + return t.underlying + } + return typ.Underlying() +} diff --git a/libgo/go/go/types/object.go b/libgo/go/go/types/object.go index 50346ec..fb37700 100644 --- a/libgo/go/go/types/object.go +++ b/libgo/go/go/types/object.go @@ -230,6 +230,26 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}} } +// _NewTypeNameLazy returns a new defined type like NewTypeName, but it +// lazily calls resolve to finish constructing the Named object. +func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { + obj := NewTypeName(pos, pkg, name, nil) + + resolve := func(_ *Context, t *Named) (*TypeParamList, Type, *methodList) { + tparams, underlying, methods := load(t) + + switch underlying.(type) { + case nil, *Named: + panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) + } + + return bindTParams(tparams), underlying, newMethodList(methods) + } + + NewNamed(obj, nil, nil).resolver = resolve + return obj +} + // IsAlias reports whether obj is an alias name for a type. func (obj *TypeName) IsAlias() bool { switch t := obj.typ.(type) { @@ -249,6 +269,8 @@ func (obj *TypeName) IsAlias() bool { return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune case *Named: return obj != t.obj + case *TypeParam: + return obj != t.obj default: return true } @@ -297,7 +319,7 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv bool // only valid for methods that don't have a type yet + hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read } // NewFunc returns a new function with the given signature, representing @@ -320,8 +342,29 @@ func (obj *Func) FullName() string { } // Scope returns the scope of the function's body block. +// The result is nil for imported or instantiated functions and methods +// (but there is also no mechanism to get to an instantiated function). func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope } +// hasPtrRecv reports whether the receiver is of the form *T for the given method obj. +func (obj *Func) hasPtrRecv() bool { + // If a method's receiver type is set, use that as the source of truth for the receiver. + // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty + // signature. We may reach here before the signature is fully set up: we must explicitly + // check if the receiver is set (we cannot just look for non-nil obj.typ). + if sig, _ := obj.typ.(*Signature); sig != nil && sig.recv != nil { + _, isPtr := deref(sig.recv.typ) + return isPtr + } + + // If a method's type is not set it may be a method/function that is: + // 1) client-supplied (via NewFunc with no signature), or + // 2) internally created but not yet type-checked. + // For case 1) we can't do anything; the client must know what they are doing. + // For case 2) we can use the information gathered by the resolver. + return obj.hasPtrRecv_ +} + func (*Func) isDependency() {} // a function may be a dependency of an initialization expression // A Label represents a declared label. @@ -370,6 +413,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { case *TypeName: tname = obj buf.WriteString("type") + if isTypeParam(typ) { + buf.WriteString(" parameter") + } case *Var: if obj.isField { @@ -415,19 +461,34 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { } if tname != nil { - // We have a type object: Don't print anything more for - // basic types since there's no more information (names - // are the same; see also comment in TypeName.IsAlias). - if _, ok := typ.(*Basic); ok { + switch t := typ.(type) { + case *Basic: + // Don't print anything more for basic types since there's + // no more information. return + case *Named: + if t.TypeParams().Len() > 0 { + newTypeWriter(buf, qf).tParamList(t.TypeParams().list()) + } } if tname.IsAlias() { buf.WriteString(" =") + } else if t, _ := typ.(*TypeParam); t != nil { + typ = t.bound } else { + // TODO(gri) should this be fromRHS for *Named? typ = under(typ) } } + // Special handling for any: because WriteType will format 'any' as 'any', + // resulting in the object string `type any = any` rather than `type any = + // interface{}`. To avoid this, swap in a different empty interface. + if obj == universeAny { + assert(Identical(typ, &emptyInterface)) + typ = &emptyInterface + } + buf.WriteByte(' ') WriteType(buf, typ, qf) } diff --git a/libgo/go/go/types/object_test.go b/libgo/go/go/types/object_test.go index 2b6057b..47c7fcd 100644 --- a/libgo/go/go/types/object_test.go +++ b/libgo/go/go/types/object_test.go @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types +package types_test import ( "go/ast" "go/parser" "go/token" + "internal/testenv" + "strings" "testing" + + . "go/types" ) func TestIsAlias(t *testing.T) { @@ -22,7 +26,7 @@ func TestIsAlias(t *testing.T) { check(Unsafe.Scope().Lookup("Pointer").(*TypeName), false) for _, name := range Universe.Names() { if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil { - check(obj, name == "byte" || name == "rune") + check(obj, name == "any" || name == "byte" || name == "rune") } } @@ -30,19 +34,22 @@ func TestIsAlias(t *testing.T) { pkg := NewPackage("p", "p") t1 := NewTypeName(0, pkg, "t1", nil) n1 := NewNamed(t1, new(Struct), nil) + t5 := NewTypeName(0, pkg, "t5", nil) + NewTypeParam(t5, nil) for _, test := range []struct { name *TypeName alias bool }{ - {NewTypeName(0, nil, "t0", nil), false}, // no type yet - {NewTypeName(0, pkg, "t0", nil), false}, // no type yet - {t1, false}, // type name refers to named type and vice versa - {NewTypeName(0, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type - {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name - {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name - {NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name - {NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe) - {NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already + {NewTypeName(0, nil, "t0", nil), false}, // no type yet + {NewTypeName(0, pkg, "t0", nil), false}, // no type yet + {t1, false}, // type name refers to named type and vice versa + {NewTypeName(0, nil, "t2", NewInterfaceType(nil, nil)), true}, // type name refers to unnamed type + {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name + {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name + {NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name + {NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe) + {NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already + {t5, false}, // type name refers to type parameter and vice versa } { check(test.name, test.alias) } @@ -84,3 +91,80 @@ func TestEmbeddedMethod(t *testing.T) { t.Fatalf("%s (%p) != %s (%p)", orig, orig, embed, embed) } } + +var testObjects = []struct { + src string + obj string + want string +}{ + {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"}, + + {"const c = 1.2", "c", "const p.c untyped float"}, + {"const c float64 = 3.14", "c", "const p.c float64"}, + + {"type t struct{f int}", "t", "type p.t struct{f int}"}, + {"type t func(int)", "t", "type p.t func(int)"}, + {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"}, + {"type t[P any] struct{f P}", "t.P", "type parameter P any"}, + {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"}, + + {"type t = struct{f int}", "t", "type p.t = struct{f int}"}, + {"type t = func(int)", "t", "type p.t = func(int)"}, + + {"var v int", "v", "var p.v int"}, + + {"func f(int) string", "f", "func p.f(int) string"}, + {"func g[P any](x P){}", "g", "func p.g[P any](x P)"}, + {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"}, + {"", "any", "type any = interface{}"}, +} + +func TestObjectString(t *testing.T) { + testenv.MustHaveGoBuild(t) + + for _, test := range testObjects { + src := "package p; " + test.src + pkg, err := makePkg(src) + if err != nil { + t.Errorf("%s: %s", src, err) + continue + } + + names := strings.Split(test.obj, ".") + if len(names) != 1 && len(names) != 2 { + t.Errorf("%s: invalid object path %s", test.src, test.obj) + continue + } + _, obj := pkg.Scope().LookupParent(names[0], token.NoPos) + if obj == nil { + t.Errorf("%s: %s not found", test.src, names[0]) + continue + } + if len(names) == 2 { + if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok { + obj = lookupTypeParamObj(typ.TypeParams(), names[1]) + if obj == nil { + t.Errorf("%s: %s not found", test.src, test.obj) + continue + } + } else { + t.Errorf("%s: %s has no type parameters", test.src, names[0]) + continue + } + } + + if got := obj.String(); got != test.want { + t.Errorf("%s: got %s, want %s", test.src, got, test.want) + } + } +} + +func lookupTypeParamObj(list *TypeParamList, name string) Object { + for i := 0; i < list.Len(); i++ { + tpar := list.At(i) + if tpar.Obj().Name() == name { + return tpar.Obj() + } + } + return nil +} diff --git a/libgo/go/go/types/operand.go b/libgo/go/go/types/operand.go index 6463728..4d7f1e3 100644 --- a/libgo/go/go/types/operand.go +++ b/libgo/go/go/types/operand.go @@ -8,7 +8,6 @@ package types import ( "bytes" - "fmt" "go/ast" "go/constant" "go/token" @@ -105,6 +104,11 @@ func (x *operand) Pos() token.Pos { // cgofunc <expr> ( <mode> of type <typ>) // func operandString(x *operand, qf Qualifier) string { + // special-case nil + if x.mode == value && x.typ == Typ[UntypedNil] { + return "nil" + } + var buf bytes.Buffer var expr string @@ -159,16 +163,17 @@ func operandString(x *operand, qf Qualifier) string { if hasType { if x.typ != Typ[Invalid] { var intro string - switch { - case isGeneric(x.typ): - intro = " of generic type " - case asTypeParam(x.typ) != nil: - intro = " of type parameter type " - default: + if isGeneric(x.typ) { + intro = " of parameterized type " + } else { intro = " of type " } buf.WriteString(intro) WriteType(&buf, x.typ, qf) + if tpar, _ := x.typ.(*TypeParam); tpar != nil { + buf.WriteString(" constrained by ") + WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here + } } else { buf.WriteString(" with invalid type") } @@ -234,61 +239,131 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er V := x.typ // x's type is identical to T - if check.identical(V, T) { + if Identical(V, T) { return true, 0 } - Vu := optype(V) - Tu := optype(T) + Vu := under(V) + Tu := under(T) + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) // x is an untyped value representable by a value of type T. if isUntyped(Vu) { - if t, ok := Tu.(*_Sum); ok { - return t.is(func(t Type) bool { - // TODO(gri) this could probably be more efficient - ok, _ := x.assignableTo(check, t, reason) - return ok + assert(Vp == nil) + if Tp != nil { + // T is a type parameter: x is assignable to T if it is + // representable by each specific type in the type set of T. + return Tp.is(func(t *term) bool { + if t == nil { + return false + } + // A term may be a tilde term but the underlying + // type of an untyped value doesn't change so we + // don't need to do anything special. + newType, _, _ := check.implicitTypeAndValue(x, t.typ) + return newType != nil }), _IncompatibleAssign } - newType, _, _ := check.implicitTypeAndValue(x, Tu) + newType, _, _ := check.implicitTypeAndValue(x, T) return newType != nil, _IncompatibleAssign } // Vu is typed // x's type V and T have identical underlying types // and at least one of V or T is not a named type - if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) { + // and neither V nor T is a type parameter. + if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) && Vp == nil && Tp == nil { return true, 0 } - // T is an interface type and x implements T - if Ti, ok := Tu.(*Interface); ok { - if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ { + // T is an interface type and x implements T and T is not a type parameter. + // Also handle the case where T is a pointer to an interface. + if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) { + if err := check.implements(V, T); err != nil { if reason != nil { - if wrongType != nil { - if check.identical(m.typ, wrongType.typ) { - *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name) - } else { - *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ) - } - - } else { - *reason = "missing method " + m.Name() - } + *reason = err.Error() } return false, _InvalidIfaceAssign } return true, 0 } + // If V is an interface, check if a missing type assertion is the problem. + if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil { + if check.implements(T, V) == nil { + // T implements V, so give hint about type assertion. + if reason != nil { + *reason = "need type assertion" + } + return false, _IncompatibleAssign + } + } + // x is a bidirectional channel value, T is a channel // type, x's type V and T have identical element types, - // and at least one of V or T is not a named type + // and at least one of V or T is not a named type. if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv { - if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) { - return !isNamed(V) || !isNamed(T), _InvalidChanAssign + if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) { + return !hasName(V) || !hasName(T), _InvalidChanAssign } } + // optimization: if we don't have type parameters, we're done + if Vp == nil && Tp == nil { + return false, _IncompatibleAssign + } + + errorf := func(format string, args ...any) { + if check != nil && reason != nil { + msg := check.sprintf(format, args...) + if *reason != "" { + msg += "\n\t" + *reason + } + *reason = msg + } + } + + // x's type V is not a named type and T is a type parameter, and + // x is assignable to each specific type in T's type set. + if !hasName(V) && Tp != nil { + ok := false + code := _IncompatibleAssign + Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + ok, code = x.assignableTo(check, T.typ, reason) + if !ok { + errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp) + return false + } + return true + }) + return ok, code + } + + // x's type V is a type parameter and T is not a named type, + // and values x' of each specific type in V's type set are + // assignable to T. + if Vp != nil && !hasName(T) { + x := *x // don't clobber outer x + ok := false + code := _IncompatibleAssign + Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + ok, code = x.assignableTo(check, T, reason) + if !ok { + errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T) + return false + } + return true + }) + return ok, code + } + return false, _IncompatibleAssign } diff --git a/libgo/go/go/types/pointer.go b/libgo/go/go/types/pointer.go new file mode 100644 index 0000000..6352ee5 --- /dev/null +++ b/libgo/go/go/types/pointer.go @@ -0,0 +1,19 @@ +// Copyright 2011 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 types + +// A Pointer represents a pointer type. +type Pointer struct { + base Type // element type +} + +// NewPointer returns a new pointer type for the given element (base) type. +func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } + +// Elem returns the element type for the given pointer p. +func (p *Pointer) Elem() Type { return p.base } + +func (t *Pointer) Underlying() Type { return t } +func (t *Pointer) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/predicates.go b/libgo/go/go/types/predicates.go index 7bb0264..1ba0043 100644 --- a/libgo/go/go/types/predicates.go +++ b/libgo/go/go/types/predicates.go @@ -6,84 +6,100 @@ package types -import ( - "go/token" -) - -// isNamed reports whether typ has a name. -// isNamed may be called with types that are not fully set up. -func isNamed(typ Type) bool { - switch typ.(type) { - case *Basic, *Named, *_TypeParam, *instance: - return true - } - return false +import "go/token" + +// The isX predicates below report whether t is an X. +// If t is a type parameter the result is false; i.e., +// these predicates don't look inside a type parameter. + +func isBoolean(t Type) bool { return isBasic(t, IsBoolean) } +func isInteger(t Type) bool { return isBasic(t, IsInteger) } +func isUnsigned(t Type) bool { return isBasic(t, IsUnsigned) } +func isFloat(t Type) bool { return isBasic(t, IsFloat) } +func isComplex(t Type) bool { return isBasic(t, IsComplex) } +func isNumeric(t Type) bool { return isBasic(t, IsNumeric) } +func isString(t Type) bool { return isBasic(t, IsString) } +func isIntegerOrFloat(t Type) bool { return isBasic(t, IsInteger|IsFloat) } +func isConstType(t Type) bool { return isBasic(t, IsConstType) } + +// isBasic reports whether under(t) is a basic type with the specified info. +// If t is a type parameter the result is false; i.e., +// isBasic does not look inside a type parameter. +func isBasic(t Type, info BasicInfo) bool { + u, _ := under(t).(*Basic) + return u != nil && u.info&info != 0 } -// isGeneric reports whether a type is a generic, uninstantiated type (generic -// signatures are not included). -func isGeneric(typ Type) bool { - // A parameterized type is only instantiated if it doesn't have an instantiation already. - named, _ := typ.(*Named) - return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil +// The allX predicates below report whether t is an X. +// If t is a type parameter the result is true if isX is true +// for all specified types of the type parameter's type set. +// allX is an optimized version of isX(structuralType(t)) (which +// is the same as underIs(t, isX)). + +func allBoolean(typ Type) bool { return allBasic(typ, IsBoolean) } +func allInteger(typ Type) bool { return allBasic(typ, IsInteger) } +func allUnsigned(typ Type) bool { return allBasic(typ, IsUnsigned) } +func allNumeric(typ Type) bool { return allBasic(typ, IsNumeric) } +func allString(typ Type) bool { return allBasic(typ, IsString) } +func allOrdered(typ Type) bool { return allBasic(typ, IsOrdered) } +func allNumericOrString(typ Type) bool { return allBasic(typ, IsNumeric|IsString) } + +// allBasic reports whether under(t) is a basic type with the specified info. +// If t is a type parameter, the result is true if isBasic(t, info) is true +// for all specific types of the type parameter's type set. +// allBasic(t, info) is an optimized version of isBasic(structuralType(t), info). +func allBasic(t Type, info BasicInfo) bool { + if tpar, _ := t.(*TypeParam); tpar != nil { + return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) }) + } + return isBasic(t, info) } -func is(typ Type, what BasicInfo) bool { - switch t := optype(typ).(type) { - case *Basic: - return t.info&what != 0 - case *_Sum: - return t.is(func(typ Type) bool { return is(typ, what) }) +// hasName reports whether t has a name. This includes +// predeclared types, defined types, and type parameters. +// hasName may be called with types that are not fully set up. +func hasName(t Type) bool { + switch t.(type) { + case *Basic, *Named, *TypeParam: + return true } return false } -func isBoolean(typ Type) bool { return is(typ, IsBoolean) } -func isInteger(typ Type) bool { return is(typ, IsInteger) } -func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) } -func isFloat(typ Type) bool { return is(typ, IsFloat) } -func isComplex(typ Type) bool { return is(typ, IsComplex) } -func isNumeric(typ Type) bool { return is(typ, IsNumeric) } -func isString(typ Type) bool { return is(typ, IsString) } - -// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not -// produce the expected result because a type list that contains both an integer -// and a floating-point type is neither (all) integers, nor (all) floats. -// Use isIntegerOrFloat instead. -func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) } - -// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ). -func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) } - -// isTyped reports whether typ is typed; i.e., not an untyped +// isTyped reports whether t is typed; i.e., not an untyped // constant or boolean. isTyped may be called with types that // are not fully set up. -func isTyped(typ Type) bool { +func isTyped(t Type) bool { // isTyped is called with types that are not fully - // set up. Must not call asBasic()! - // A *Named or *instance type is always typed, so - // we only need to check if we have a true *Basic - // type. - t, _ := typ.(*Basic) - return t == nil || t.info&IsUntyped == 0 + // set up. Must not call under()! + b, _ := t.(*Basic) + return b == nil || b.info&IsUntyped == 0 } -// isUntyped(typ) is the same as !isTyped(typ). -func isUntyped(typ Type) bool { - return !isTyped(typ) +// isUntyped(t) is the same as !isTyped(t). +func isUntyped(t Type) bool { + return !isTyped(t) } -func isOrdered(typ Type) bool { return is(typ, IsOrdered) } +// IsInterface reports whether t is an interface type. +func IsInterface(t Type) bool { + _, ok := under(t).(*Interface) + return ok +} -func isConstType(typ Type) bool { - // Type parameters are never const types. - t, _ := under(typ).(*Basic) - return t != nil && t.info&IsConstType != 0 +// isTypeParam reports whether t is a type parameter. +func isTypeParam(t Type) bool { + _, ok := t.(*TypeParam) + return ok } -// IsInterface reports whether typ is an interface type. -func IsInterface(typ Type) bool { - return asInterface(typ) != nil +// isGeneric reports whether a type is a generic, uninstantiated type +// (generic signatures are not included). +// TODO(gri) should we include signatures or assert that they are not present? +func isGeneric(t Type) bool { + // A parameterized type is only generic if it doesn't have an instantiation already. + named, _ := t.(*Named) + return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil } // Comparable reports whether values of type T are comparable. @@ -100,24 +116,12 @@ func comparable(T Type, seen map[Type]bool) bool { } seen[T] = true - // If T is a type parameter not constrained by any type - // list (i.e., it's underlying type is the top type), - // T is comparable if it has the == method. Otherwise, - // the underlying type "wins". For instance - // - // interface{ comparable; type []byte } - // - // is not comparable because []byte is not comparable. - if t := asTypeParam(T); t != nil && optype(t) == theTop { - return t.Bound()._IsComparable() - } - - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Basic: // assume invalid types to be comparable // to avoid follow-up errors return t.kind != UntypedNil - case *Pointer, *Interface, *Chan: + case *Pointer, *Chan: return true case *Struct: for _, f := range t.fields { @@ -128,42 +132,27 @@ func comparable(T Type, seen map[Type]bool) bool { return true case *Array: return comparable(t.elem, seen) - case *_Sum: - pred := func(t Type) bool { - return comparable(t, seen) - } - return t.is(pred) - case *_TypeParam: - return t.Bound()._IsComparable() + case *Interface: + return !isTypeParam(T) || t.typeSet().IsComparable(seen) } return false } -// hasNil reports whether a type includes the nil value. -func hasNil(typ Type) bool { - switch t := optype(typ).(type) { +// hasNil reports whether type t includes the nil value. +func hasNil(t Type) bool { + switch u := under(t).(type) { case *Basic: - return t.kind == UnsafePointer - case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: + return u.kind == UnsafePointer + case *Slice, *Pointer, *Signature, *Map, *Chan: return true - case *_Sum: - return t.is(hasNil) + case *Interface: + return !isTypeParam(t) || u.typeSet().underIs(func(u Type) bool { + return u != nil && hasNil(u) + }) } return false } -// identical reports whether x and y are identical types. -// Receivers of Signature types are ignored. -func (check *Checker) identical(x, y Type) bool { - return check.identical0(x, y, true, nil) -} - -// identicalIgnoreTags reports whether x and y are identical types if tags are ignored. -// Receivers of Signature types are ignored. -func (check *Checker) identicalIgnoreTags(x, y Type) bool { - return check.identical0(x, y, false, nil) -} - // An ifacePair is a node in a stack of interface type pairs compared for identity. type ifacePair struct { x, y *Interface @@ -175,11 +164,7 @@ func (p *ifacePair) identical(q *ifacePair) bool { } // For changes to this code the corresponding changes should be made to unifier.nify. -func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { - // types must be expanded for comparison - x = expandf(x) - y = expandf(y) - +func identical(x, y Type, cmpTags bool, p *ifacePair) bool { if x == y { return true } @@ -199,13 +184,13 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if y, ok := y.(*Array); ok { // If one or both array lengths are unknown (< 0) due to some error, // assume they are the same to avoid spurious follow-on errors. - return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p) + return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p) } case *Slice: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { - return check.identical0(x.elem, y.elem, cmpTags, p) + return identical(x.elem, y.elem, cmpTags, p) } case *Struct: @@ -220,7 +205,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if f.embedded != g.embedded || cmpTags && x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || - !check.identical0(f.typ, g.typ, cmpTags, p) { + !identical(f.typ, g.typ, cmpTags, p) { return false } } @@ -231,7 +216,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { - return check.identical0(x.base, y.base, cmpTags, p) + return identical(x.base, y.base, cmpTags, p) } case *Tuple: @@ -242,7 +227,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if x != nil { for i, v := range x.vars { w := y.vars[i] - if !check.identical0(v.typ, w.typ, cmpTags, p) { + if !identical(v.typ, w.typ, cmpTags, p) { return false } } @@ -252,57 +237,82 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { } case *Signature: - // Two function types are identical if they have the same number of parameters - // and result values, corresponding parameter and result types are identical, - // and either both functions are variadic or neither is. Parameter and result - // names are not required to match. - // Generic functions must also have matching type parameter lists, but for the - // parameter names. - if y, ok := y.(*Signature); ok { - return x.variadic == y.variadic && - check.identicalTParams(x.tparams, y.tparams, cmpTags, p) && - check.identical0(x.params, y.params, cmpTags, p) && - check.identical0(x.results, y.results, cmpTags, p) + y, _ := y.(*Signature) + if y == nil { + return false } - case *_Sum: - // Two sum types are identical if they contain the same types. - // (Sum types always consist of at least two types. Also, the - // the set (list) of types in a sum type consists of unique - // types - each type appears exactly once. Thus, two sum types - // must contain the same number of types to have chance of - // being equal. - if y, ok := y.(*_Sum); ok && len(x.types) == len(y.types) { - // Every type in x.types must be in y.types. - // Quadratic algorithm, but probably good enough for now. - // TODO(gri) we need a fast quick type ID/hash for all types. - L: - for _, x := range x.types { - for _, y := range y.types { - if Identical(x, y) { - continue L // x is in y.types - } + // Two function types are identical if they have the same number of + // parameters and result values, corresponding parameter and result types + // are identical, and either both functions are variadic or neither is. + // Parameter and result names are not required to match, and type + // parameters are considered identical modulo renaming. + + if x.TypeParams().Len() != y.TypeParams().Len() { + return false + } + + // In the case of generic signatures, we will substitute in yparams and + // yresults. + yparams := y.params + yresults := y.results + + if x.TypeParams().Len() > 0 { + // We must ignore type parameter names when comparing x and y. The + // easiest way to do this is to substitute x's type parameters for y's. + xtparams := x.TypeParams().list() + ytparams := y.TypeParams().list() + + var targs []Type + for i := range xtparams { + targs = append(targs, x.TypeParams().At(i)) + } + smap := makeSubstMap(ytparams, targs) + + var check *Checker // ok to call subst on a nil *Checker + + // Constraints must be pair-wise identical, after substitution. + for i, xtparam := range xtparams { + ybound := check.subst(token.NoPos, ytparams[i].bound, smap, nil) + if !identical(xtparam.bound, ybound, cmpTags, p) { + return false } - return false // x is not in y.types } - return true + + yparams = check.subst(token.NoPos, y.params, smap, nil).(*Tuple) + yresults = check.subst(token.NoPos, y.results, smap, nil).(*Tuple) + } + + return x.variadic == y.variadic && + identical(x.params, yparams, cmpTags, p) && + identical(x.results, yresults, cmpTags, p) + + case *Union: + if y, _ := y.(*Union); y != nil { + // TODO(rfindley): can this be reached during type checking? If so, + // consider passing a type set map. + unionSets := make(map[*Union]*_TypeSet) + xset := computeUnionTypeSet(nil, unionSets, token.NoPos, x) + yset := computeUnionTypeSet(nil, unionSets, token.NoPos, y) + return xset.terms.equal(yset.terms) } case *Interface: + // Two interface types are identical if they describe the same type sets. + // With the existing implementation restriction, this simplifies to: + // // Two interface types are identical if they have the same set of methods with - // the same names and identical function types. Lower-case method names from - // different packages are always different. The order of the methods is irrelevant. + // the same names and identical function types, and if any type restrictions + // are the same. Lower-case method names from different packages are always + // different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - // If identical0 is called (indirectly) via an external API entry point - // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in - // that case, interfaces are expected to be complete and lazy completion - // here is not needed. - if check != nil { - check.completeInterface(token.NoPos, x) - check.completeInterface(token.NoPos, y) + xset := x.typeSet() + yset := y.typeSet() + if !xset.terms.equal(yset.terms) { + return false } - a := x.allMethods - b := y.allMethods + a := xset.methods + b := yset.methods if len(a) == len(b) { // Interface types are the only types where cycles can occur // that are not "terminated" via named types; and such cycles @@ -339,7 +349,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { } for i, f := range a { g := b[i] - if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) { + if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) { return false } } @@ -350,37 +360,50 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Map: // Two map types are identical if they have identical key and value types. if y, ok := y.(*Map); ok { - return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p) + return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p) } case *Chan: // Two channel types are identical if they have identical value types // and the same direction. if y, ok := y.(*Chan); ok { - return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p) + return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p) } case *Named: // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() + + if len(xargs) != len(yargs) { + return false + } + + if len(xargs) > 0 { + // Instances are identical if their original type and type arguments + // are identical. + if !Identical(x.orig, y.orig) { + return false + } + for i, xa := range xargs { + if !Identical(xa, yargs[i]) { + return false + } + } + return true + } + // TODO(gri) Why is x == y not sufficient? And if it is, // we can just return false here because x == y // is caught in the very beginning of this function. return x.obj == y.obj } - case *_TypeParam: + case *TypeParam: // nothing to do (x and y being equal is caught in the very beginning of this function) - // case *instance: - // unreachable since types are expanded - - case *bottom, *top: - // Either both types are theBottom, or both are theTop in which - // case the initial x == y check will have caught them. Otherwise - // they are not identical. - case nil: // avoid a crash in case of nil type @@ -391,25 +414,28 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { return false } -func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool { - if len(x) != len(y) { +// identicalInstance reports if two type instantiations are identical. +// Instantiations are identical if their origin and type arguments are +// identical. +func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool { + if len(xargs) != len(yargs) { return false } - for i, x := range x { - y := y[i] - if !check.identical0(x.typ.(*_TypeParam).bound, y.typ.(*_TypeParam).bound, cmpTags, p) { + + for i, xa := range xargs { + if !Identical(xa, yargs[i]) { return false } } - return true + + return Identical(xorig, yorig) } // Default returns the default "typed" type for an "untyped" type; // it returns the incoming type for all other types. The default type // for untyped nil is untyped nil. -// -func Default(typ Type) Type { - if t, ok := typ.(*Basic); ok { +func Default(t Type) Type { + if t, ok := t.(*Basic); ok { switch t.kind { case UntypedBool: return Typ[Bool] @@ -425,5 +451,5 @@ func Default(typ Type) Type { return Typ[String] } } - return typ + return t } diff --git a/libgo/go/go/types/resolver.go b/libgo/go/go/types/resolver.go index 4892218..9edf41b 100644 --- a/libgo/go/go/types/resolver.go +++ b/libgo/go/go/types/resolver.go @@ -309,20 +309,24 @@ func (check *Checker) collectObjects() { check.dotImportMap = make(map[dotImportKey]*PkgName) } // merge imported scope with file scope - for _, obj := range imp.scope.elems { + for name, obj := range imp.scope.elems { + // Note: Avoid eager resolve(name, obj) here, so we only + // resolve dot-imported objects as needed. + // A package scope may contain non-exported objects, // do not import them! - if obj.Exported() { + if token.IsExported(name) { // declare dot-imported object // (Do not use check.declare because it modifies the object // via Object.setScopePos, which leads to a race condition; // the object may be imported into more than one file scope // concurrently. See issue #32154.) - if alt := fileScope.Insert(obj); alt != nil { - check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name()) + if alt := fileScope.Lookup(name); alt != nil { + check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", alt.Name()) check.reportAltDecl(alt) } else { - check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName + fileScope.insert(name, obj) + check.dotImportMap[dotImportKey{fileScope, name}] = pkgName } } } @@ -377,12 +381,15 @@ func (check *Checker) collectObjects() { check.declarePkgObj(name, obj, di) } case typeDecl: + if d.spec.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) { + check.softErrorf(d.spec.TypeParams.List[0], _UnsupportedFeature, "type parameters require go1.18 or later") + } obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil) check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec}) case funcDecl: - info := &declInfo{file: fileScope, fdecl: d.decl} name := d.decl.Name.Name obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil) + hasTParamError := false // avoid duplicate type parameter errors if d.decl.Recv.NumFields() == 0 { // regular function if d.decl.Recv != nil { @@ -394,8 +401,9 @@ func (check *Checker) collectObjects() { if name == "main" { code = _InvalidMainDecl } - if tparams := typeparams.Get(d.decl.Type); tparams != nil { - check.softErrorf(tparams, code, "func %s must have no type parameters", name) + if d.decl.Type.TypeParams.NumFields() != 0 { + check.softErrorf(d.decl.Type.TypeParams.List[0], code, "func %s must have no type parameters", name) + hasTParamError = true } if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil { // TODO(rFindley) Should this be a hard error? @@ -431,6 +439,10 @@ func (check *Checker) collectObjects() { } check.recordDef(d.decl.Name, obj) } + if d.decl.Type.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError { + check.softErrorf(d.decl.Type.TypeParams.List[0], _UnsupportedFeature, "type parameters require go1.18 or later") + } + info := &declInfo{file: fileScope, fdecl: d.decl} // Methods are not package-level objects but we still track them in the // object map so that we can handle them like regular functions (if the // receiver is invalid); also we need their fdecl info when associating @@ -443,8 +455,9 @@ func (check *Checker) collectObjects() { // verify that objects in package and file scopes have different names for _, scope := range fileScopes { - for _, obj := range scope.elems { - if alt := pkg.scope.Lookup(obj.Name()); alt != nil { + for name, obj := range scope.elems { + if alt := pkg.scope.Lookup(name); alt != nil { + obj = resolve(name, obj) if pkg, ok := obj.(*PkgName); ok { check.errorf(alt, _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported()) check.reportAltDecl(pkg) @@ -470,7 +483,7 @@ func (check *Checker) collectObjects() { // Determine the receiver base type and associate m with it. ptr, base := check.resolveBaseTypeName(m.ptr, m.recv) if base != nil { - m.obj.hasPtrRecv = ptr + m.obj.hasPtrRecv_ = ptr check.methods[base] = append(check.methods[base], m.obj) } } @@ -499,10 +512,12 @@ L: // unpack receiver type } // unpack type parameters, if any - if ptyp, _ := rtyp.(*ast.IndexExpr); ptyp != nil { - rtyp = ptyp.X + switch rtyp.(type) { + case *ast.IndexExpr, *ast.IndexListExpr: + ix := typeparams.UnpackIndexExpr(rtyp) + rtyp = ix.X if unpackParams { - for _, arg := range typeparams.UnpackExpr(ptyp.Index) { + for _, arg := range ix.Indices { var par *ast.Ident switch arg := arg.(type) { case *ast.Ident: @@ -510,9 +525,9 @@ L: // unpack receiver type case *ast.BadExpr: // ignore - error already reported by parser case nil: - check.invalidAST(ptyp, "parameterized receiver contains nil parameters") + check.invalidAST(ix.Orig, "parameterized receiver contains nil parameters") default: - check.errorf(arg, _Todo, "receiver type parameter %s must be an identifier", arg) + check.errorf(arg, _BadDecl, "receiver type parameter %s must be an identifier", arg) } if par == nil { par = &ast.Ident{NamePos: arg.Pos(), Name: "_"} @@ -614,27 +629,33 @@ func (check *Checker) packageObjects() { } } - // We process non-alias declarations first, in order to avoid situations where - // the type of an alias declaration is needed before it is available. In general - // this is still not enough, as it is possible to create sufficiently convoluted - // recursive type definitions that will cause a type alias to be needed before it - // is available (see issue #25838 for examples). - // As an aside, the cmd/compiler suffers from the same problem (#25838). + // We process non-alias type declarations first, followed by alias declarations, + // and then everything else. This appears to avoid most situations where the type + // of an alias is needed before it is available. + // There may still be cases where this is not good enough (see also issue #25838). + // In those cases Checker.ident will report an error ("invalid use of type alias"). var aliasList []*TypeName - // phase 1 + var othersList []Object // everything that's not a type + // phase 1: non-alias type declarations for _, obj := range objList { - // If we have a type alias, collect it for the 2nd phase. - if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].tdecl.Assign.IsValid() { - aliasList = append(aliasList, tname) - continue + if tname, _ := obj.(*TypeName); tname != nil { + if check.objMap[tname].tdecl.Assign.IsValid() { + aliasList = append(aliasList, tname) + } else { + check.objDecl(obj, nil) + } + } else { + othersList = append(othersList, obj) } - - check.objDecl(obj, nil) } - // phase 2 + // phase 2: alias type declarations for _, obj := range aliasList { check.objDecl(obj, nil) } + // phase 3: all other declarations + for _, obj := range othersList { + check.objDecl(obj, nil) + } // At this point we may have a non-empty check.methods map; this means that not all // entries were deleted at the end of typeDecl because the respective receiver base diff --git a/libgo/go/go/types/sanitize.go b/libgo/go/go/types/sanitize.go deleted file mode 100644 index 727ec17..0000000 --- a/libgo/go/go/types/sanitize.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2020 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 types - -// sanitizeInfo walks the types contained in info to ensure that all instances -// are expanded. -// -// This includes some objects that may be shared across concurrent -// type-checking passes (such as those in the universe scope), so we are -// careful here not to write types that are already sanitized. This avoids a -// data race as any shared types should already be sanitized. -func sanitizeInfo(info *Info) { - var s sanitizer = make(map[Type]Type) - - // Note: Some map entries are not references. - // If modified, they must be assigned back. - - for e, tv := range info.Types { - if typ := s.typ(tv.Type); typ != tv.Type { - tv.Type = typ - info.Types[e] = tv - } - } - - inferred := getInferred(info) - for e, inf := range inferred { - changed := false - for i, targ := range inf.Targs { - if typ := s.typ(targ); typ != targ { - inf.Targs[i] = typ - changed = true - } - } - if typ := s.typ(inf.Sig); typ != inf.Sig { - inf.Sig = typ.(*Signature) - changed = true - } - if changed { - inferred[e] = inf - } - } - - for _, obj := range info.Defs { - if obj != nil { - if typ := s.typ(obj.Type()); typ != obj.Type() { - obj.setType(typ) - } - } - } - - for _, obj := range info.Uses { - if obj != nil { - if typ := s.typ(obj.Type()); typ != obj.Type() { - obj.setType(typ) - } - } - } - - // TODO(gri) sanitize as needed - // - info.Implicits - // - info.Selections - // - info.Scopes - // - info.InitOrder -} - -type sanitizer map[Type]Type - -func (s sanitizer) typ(typ Type) Type { - if typ == nil { - return nil - } - - if t, found := s[typ]; found { - return t - } - s[typ] = typ - - switch t := typ.(type) { - case *Basic, *bottom, *top: - // nothing to do - - case *Array: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Slice: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Struct: - s.varList(t.fields) - - case *Pointer: - if base := s.typ(t.base); base != t.base { - t.base = base - } - - case *Tuple: - s.tuple(t) - - case *Signature: - s.var_(t.recv) - s.tuple(t.params) - s.tuple(t.results) - - case *_Sum: - s.typeList(t.types) - - case *Interface: - s.funcList(t.methods) - if types := s.typ(t.types); types != t.types { - t.types = types - } - s.typeList(t.embeddeds) - s.funcList(t.allMethods) - if allTypes := s.typ(t.allTypes); allTypes != t.allTypes { - t.allTypes = allTypes - } - - case *Map: - if key := s.typ(t.key); key != t.key { - t.key = key - } - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Chan: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Named: - if debug && t.check != nil { - panic("internal error: Named.check != nil") - } - if orig := s.typ(t.orig); orig != t.orig { - t.orig = orig - } - if under := s.typ(t.underlying); under != t.underlying { - t.underlying = under - } - s.typeList(t.targs) - s.funcList(t.methods) - - case *_TypeParam: - if bound := s.typ(t.bound); bound != t.bound { - t.bound = bound - } - - case *instance: - typ = t.expand() - s[t] = typ - - default: - panic("unimplemented") - } - - return typ -} - -func (s sanitizer) var_(v *Var) { - if v != nil { - if typ := s.typ(v.typ); typ != v.typ { - v.typ = typ - } - } -} - -func (s sanitizer) varList(list []*Var) { - for _, v := range list { - s.var_(v) - } -} - -func (s sanitizer) tuple(t *Tuple) { - if t != nil { - s.varList(t.vars) - } -} - -func (s sanitizer) func_(f *Func) { - if f != nil { - if typ := s.typ(f.typ); typ != f.typ { - f.typ = typ - } - } -} - -func (s sanitizer) funcList(list []*Func) { - for _, f := range list { - s.func_(f) - } -} - -func (s sanitizer) typeList(list []Type) { - for i, t := range list { - if typ := s.typ(t); typ != t { - list[i] = typ - } - } -} diff --git a/libgo/go/go/types/scope.go b/libgo/go/go/types/scope.go index 26c28d1..010727e 100644 --- a/libgo/go/go/types/scope.go +++ b/libgo/go/go/types/scope.go @@ -13,6 +13,7 @@ import ( "io" "sort" "strings" + "sync" ) // A Scope maintains a set of objects and links to its containing @@ -22,6 +23,7 @@ import ( type Scope struct { parent *Scope children []*Scope + number int // parent.children[number-1] is this scope; 0 if there is no parent elems map[string]Object // lazily allocated pos, end token.Pos // scope extent; may be invalid comment string // for debugging only @@ -31,10 +33,11 @@ type Scope struct { // NewScope returns a new, empty scope contained in the given parent // scope, if any. The comment is for debugging only. func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope { - s := &Scope{parent, nil, nil, pos, end, comment, false} + s := &Scope{parent, nil, 0, nil, pos, end, comment, false} // don't add children to Universe scope! if parent != nil && parent != Universe { parent.children = append(parent.children, s) + s.number = len(parent.children) } return s } @@ -66,7 +69,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] } // Lookup returns the object in scope s with the given name if such an // object exists; otherwise the result is nil. func (s *Scope) Lookup(name string) Object { - return s.elems[name] + return resolve(name, s.elems[name]) } // LookupParent follows the parent chain of scopes starting with s until @@ -81,7 +84,7 @@ func (s *Scope) Lookup(name string) Object { // whose scope is the scope of the package that exported them. func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) { for ; s != nil; s = s.parent { - if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) { + if obj := s.Lookup(name); obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) { return s, obj } } @@ -95,19 +98,38 @@ func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) { // if not already set, and returns nil. func (s *Scope) Insert(obj Object) Object { name := obj.Name() - if alt := s.elems[name]; alt != nil { + if alt := s.Lookup(name); alt != nil { return alt } - if s.elems == nil { - s.elems = make(map[string]Object) - } - s.elems[name] = obj + s.insert(name, obj) if obj.Parent() == nil { obj.setParent(s) } return nil } +// _InsertLazy is like Insert, but allows deferring construction of the +// inserted object until it's accessed with Lookup. The Object +// returned by resolve must have the same name as given to _InsertLazy. +// If s already contains an alternative object with the same name, +// _InsertLazy leaves s unchanged and returns false. Otherwise it +// records the binding and returns true. The object's parent scope +// will be set to s after resolve is called. +func (s *Scope) _InsertLazy(name string, resolve func() Object) bool { + if s.elems[name] != nil { + return false + } + s.insert(name, &lazyObject{parent: s, resolve: resolve}) + return true +} + +func (s *Scope) insert(name string, obj Object) { + if s.elems == nil { + s.elems = make(map[string]Object) + } + s.elems[name] = obj +} + // squash merges s with its parent scope p by adding all // objects of s to p, adding all children of s to the // children of p, and removing s from p's children. @@ -117,7 +139,8 @@ func (s *Scope) Insert(obj Object) Object { func (s *Scope) squash(err func(obj, alt Object)) { p := s.parent assert(p != nil) - for _, obj := range s.elems { + for name, obj := range s.elems { + obj = resolve(name, obj) obj.setParent(nil) if alt := p.Insert(obj); alt != nil { err(obj, alt) @@ -196,7 +219,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { indn1 := indn + ind for _, name := range s.Names() { - fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name]) + fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name)) } if recurse { @@ -214,3 +237,57 @@ func (s *Scope) String() string { s.WriteTo(&buf, 0, false) return buf.String() } + +// A lazyObject represents an imported Object that has not been fully +// resolved yet by its importer. +type lazyObject struct { + parent *Scope + resolve func() Object + obj Object + once sync.Once +} + +// resolve returns the Object represented by obj, resolving lazy +// objects as appropriate. +func resolve(name string, obj Object) Object { + if lazy, ok := obj.(*lazyObject); ok { + lazy.once.Do(func() { + obj := lazy.resolve() + + if _, ok := obj.(*lazyObject); ok { + panic("recursive lazy object") + } + if obj.Name() != name { + panic("lazy object has unexpected name") + } + + if obj.Parent() == nil { + obj.setParent(lazy.parent) + } + lazy.obj = obj + }) + + obj = lazy.obj + } + return obj +} + +// stub implementations so *lazyObject implements Object and we can +// store them directly into Scope.elems. +func (*lazyObject) Parent() *Scope { panic("unreachable") } +func (*lazyObject) Pos() token.Pos { panic("unreachable") } +func (*lazyObject) Pkg() *Package { panic("unreachable") } +func (*lazyObject) Name() string { panic("unreachable") } +func (*lazyObject) Type() Type { panic("unreachable") } +func (*lazyObject) Exported() bool { panic("unreachable") } +func (*lazyObject) Id() string { panic("unreachable") } +func (*lazyObject) String() string { panic("unreachable") } +func (*lazyObject) order() uint32 { panic("unreachable") } +func (*lazyObject) color() color { panic("unreachable") } +func (*lazyObject) setType(Type) { panic("unreachable") } +func (*lazyObject) setOrder(uint32) { panic("unreachable") } +func (*lazyObject) setColor(color color) { panic("unreachable") } +func (*lazyObject) setParent(*Scope) { panic("unreachable") } +func (*lazyObject) sameId(pkg *Package, name string) bool { panic("unreachable") } +func (*lazyObject) scopePos() token.Pos { panic("unreachable") } +func (*lazyObject) setScopePos(pos token.Pos) { panic("unreachable") } diff --git a/libgo/go/go/types/self_test.go b/libgo/go/go/types/self_test.go index 55a8312..f5fc321 100644 --- a/libgo/go/go/types/self_test.go +++ b/libgo/go/go/types/self_test.go @@ -29,12 +29,7 @@ func TestSelf(t *testing.T) { conf := Config{Importer: importer.Default()} _, err = conf.Check("go/types", fset, files, nil) if err != nil { - // Importing go/constant doesn't work in the - // build dashboard environment. Don't report an error - // for now so that the build remains green. - // TODO(gri) fix this - t.Log(err) // replace w/ t.Fatal eventually - return + t.Fatal(err) } } @@ -43,6 +38,7 @@ func BenchmarkCheck(b *testing.B) { "net/http", "go/parser", "go/constant", + "runtime", filepath.Join("go", "internal", "gcimporter"), } { b.Run(path.Base(p), func(b *testing.B) { diff --git a/libgo/go/go/types/signature.go b/libgo/go/go/types/signature.go new file mode 100644 index 0000000..8f89e93 --- /dev/null +++ b/libgo/go/go/types/signature.go @@ -0,0 +1,320 @@ +// 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 types + +import ( + "go/ast" + "go/token" +) + +// ---------------------------------------------------------------------------- +// API + +// A Signature represents a (non-builtin) function or method type. +// The receiver is ignored when comparing signatures for identity. +type Signature struct { + // We need to keep the scope in Signature (rather than passing it around + // and store it in the Func Object) because when type-checking a function + // literal we call the general type checker which returns a general Type. + // We then unpack the *Signature and use the scope for the literal body. + rparams *TypeParamList // receiver type parameters from left to right, or nil + tparams *TypeParamList // type parameters from left to right, or nil + scope *Scope // function scope for package-local and non-instantiated signatures; nil otherwise + recv *Var // nil if not a method + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) +} + +// NewSignature returns a new function type for the given receiver, parameters, +// and results, either of which may be nil. If variadic is set, the function +// is variadic, it must have at least one parameter, and the last parameter +// must be of unnamed slice type. +// +// Deprecated: Use NewSignatureType instead which allows for type parameters. +func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { + return NewSignatureType(recv, nil, nil, params, results, variadic) +} + +// NewSignatureType creates a new function type for the given receiver, +// receiver type parameters, type parameters, parameters, and results. If +// variadic is set, params must hold at least one parameter and the last +// parameter must be of unnamed slice type. If recv is non-nil, typeParams must +// be empty. If recvTypeParams is non-empty, recv must be non-nil. +func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature { + if variadic { + n := params.Len() + if n == 0 { + panic("variadic function must have at least one parameter") + } + if _, ok := params.At(n - 1).typ.(*Slice); !ok { + panic("variadic parameter must be of unnamed slice type") + } + } + sig := &Signature{recv: recv, params: params, results: results, variadic: variadic} + if len(recvTypeParams) != 0 { + if recv == nil { + panic("function with receiver type parameters must have a receiver") + } + sig.rparams = bindTParams(recvTypeParams) + } + if len(typeParams) != 0 { + if recv != nil { + panic("function with type parameters cannot have a receiver") + } + sig.tparams = bindTParams(typeParams) + } + return sig +} + +// Recv returns the receiver of signature s (if a method), or nil if a +// function. It is ignored when comparing signatures for identity. +// +// For an abstract method, Recv returns the enclosing interface either +// as a *Named or an *Interface. Due to embedding, an interface may +// contain methods whose receiver type is a different interface. +func (s *Signature) Recv() *Var { return s.recv } + +// TypeParams returns the type parameters of signature s, or nil. +func (s *Signature) TypeParams() *TypeParamList { return s.tparams } + +// RecvTypeParams returns the receiver type parameters of signature s, or nil. +func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams } + +// Params returns the parameters of signature s, or nil. +func (s *Signature) Params() *Tuple { return s.params } + +// Results returns the results of signature s, or nil. +func (s *Signature) Results() *Tuple { return s.results } + +// Variadic reports whether the signature s is variadic. +func (s *Signature) Variadic() bool { return s.variadic } + +func (t *Signature) Underlying() Type { return t } +func (t *Signature) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +// funcType type-checks a function or method type. +func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) { + check.openScope(ftyp, "function") + check.scope.isFunc = true + check.recordScope(ftyp, check.scope) + sig.scope = check.scope + defer check.closeScope() + + if recvPar != nil && len(recvPar.List) > 0 { + // collect generic receiver type parameters, if any + // - a receiver type parameter is like any other type parameter, except that it is declared implicitly + // - the receiver specification acts as local declaration for its type parameters, which may be blank + _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true) + if len(rparams) > 0 { + sig.rparams = bindTParams(check.declareTypeParams(nil, rparams)) + // Blank identifiers don't get declared, so naive type-checking of the + // receiver type expression would fail in Checker.collectParams below, + // when Checker.ident cannot resolve the _ to a type. + // + // Checker.recvTParamMap maps these blank identifiers to their type parameter + // types, so that they may be resolved in Checker.ident when they fail + // lookup in the scope. + for i, p := range rparams { + if p.Name == "_" { + tpar := sig.rparams.At(i) + if check.recvTParamMap == nil { + check.recvTParamMap = make(map[*ast.Ident]*TypeParam) + } + check.recvTParamMap[p] = tpar + } + } + // determine receiver type to get its type parameters + // and the respective type parameter bounds + var recvTParams []*TypeParam + if rname != nil { + // recv should be a Named type (otherwise an error is reported elsewhere) + // Also: Don't report an error via genericType since it will be reported + // again when we type-check the signature. + // TODO(gri) maybe the receiver should be marked as invalid instead? + if recv, _ := check.genericType(rname, nil).(*Named); recv != nil { + recvTParams = recv.TypeParams().list() + } + } + // provide type parameter bounds + // - only do this if we have the right number (otherwise an error is reported elsewhere) + if sig.RecvTypeParams().Len() == len(recvTParams) { + // We have a list of *TypeNames but we need a list of Types. + list := make([]Type, sig.RecvTypeParams().Len()) + for i, t := range sig.RecvTypeParams().list() { + list[i] = t + check.mono.recordCanon(t, recvTParams[i]) + } + smap := makeSubstMap(recvTParams, list) + for i, tpar := range sig.RecvTypeParams().list() { + bound := recvTParams[i].bound + // bound is (possibly) parameterized in the context of the + // receiver type declaration. Substitute parameters for the + // current context. + tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil) + } + } + } + } + + if ftyp.TypeParams != nil { + check.collectTypeParams(&sig.tparams, ftyp.TypeParams) + // Always type-check method type parameters but complain that they are not allowed. + // (A separate check is needed when type-checking interface method signatures because + // they don't have a receiver specification.) + if recvPar != nil { + check.errorf(ftyp.TypeParams, _InvalidMethodTypeParams, "methods cannot have type parameters") + } + } + + // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their + // declarations and then squash that scope into the parent scope (and report any redeclarations at + // that time). + scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)") + recvList, _ := check.collectParams(scope, recvPar, false) + params, variadic := check.collectParams(scope, ftyp.Params, true) + results, _ := check.collectParams(scope, ftyp.Results, false) + scope.squash(func(obj, alt Object) { + check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name()) + check.reportAltDecl(alt) + }) + + if recvPar != nil { + // recv parameter list present (may be empty) + // spec: "The receiver is specified via an extra parameter section preceding the + // method name. That parameter section must declare a single parameter, the receiver." + var recv *Var + switch len(recvList) { + case 0: + // error reported by resolver + recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below + default: + // more than one receiver + check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver") + fallthrough // continue with first receiver + case 1: + recv = recvList[0] + } + + // TODO(gri) We should delay rtyp expansion to when we actually need the + // receiver; thus all checks here should be delayed to later. + rtyp, _ := deref(recv.typ) + + // spec: "The receiver type must be of the form T or *T where T is a type name." + // (ignore invalid types - error was reported before) + if rtyp != Typ[Invalid] { + var err string + switch T := rtyp.(type) { + case *Named: + T.resolve(check.bestContext(nil)) + // The receiver type may be an instantiated type referred to + // by an alias (which cannot have receiver parameters for now). + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { + check.errorf(atPos(recv.pos), _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ) + break + } + // spec: "The type denoted by T is called the receiver base type; it must not + // be a pointer or interface type and it must be declared in the same package + // as the method." + if T.obj.pkg != check.pkg { + err = "type not defined in this package" + } else { + // The underlying type of a receiver base type can be a type parameter; + // e.g. for methods with a generic receiver T[P] with type T[P any] P. + underIs(T, func(u Type) bool { + switch u := u.(type) { + case *Basic: + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + err = "unsafe.Pointer" + return false + } + case *Pointer, *Interface: + err = "pointer or interface type" + return false + } + return true + }) + } + case *Basic: + err = "basic or unnamed type" + default: + check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ) + } + if err != "" { + check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err) + // ok to continue + } + } + sig.recv = recv + } + + sig.params = NewTuple(params...) + sig.results = NewTuple(results...) + sig.variadic = variadic +} + +// collectParams declares the parameters of list in scope and returns the corresponding +// variable list. +func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { + if list == nil { + return + } + + var named, anonymous bool + for i, field := range list.List { + ftype := field.Type + if t, _ := ftype.(*ast.Ellipsis); t != nil { + ftype = t.Elt + if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 { + variadic = true + } else { + check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list") + // ignore ... and continue + } + } + typ := check.varType(ftype) + // The parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag. + if len(field.Names) > 0 { + // named parameter + for _, name := range field.Names { + if name.Name == "" { + check.invalidAST(name, "anonymous parameter") + // ok to continue + } + par := NewParam(name.Pos(), check.pkg, name.Name, typ) + check.declare(scope, name, par, scope.pos) + params = append(params, par) + } + named = true + } else { + // anonymous parameter + par := NewParam(ftype.Pos(), check.pkg, "", typ) + check.recordImplicit(field, par) + params = append(params, par) + anonymous = true + } + } + + if named && anonymous { + check.invalidAST(list, "list contains both named and anonymous parameters") + // ok to continue + } + + // For a variadic function, change the last parameter's type from T to []T. + // Since we type-checked T rather than ...T, we also need to retro-actively + // record the type for ...T. + if variadic { + last := params[len(params)-1] + last.typ = &Slice{elem: last.typ} + check.recordTypeAndValue(list.List[len(list.List)-1].Type, typexpr, last.typ, nil) + } + + return +} diff --git a/libgo/go/go/types/sizeof_test.go b/libgo/go/go/types/sizeof_test.go index 5a9d07c..bfd14a8 100644 --- a/libgo/go/go/types/sizeof_test.go +++ b/libgo/go/go/types/sizeof_test.go @@ -14,9 +14,9 @@ func TestSizeof(t *testing.T) { const _64bit = ^uint(0)>>32 != 0 var tests = []struct { - val interface{} // type as a value - _32bit uintptr // size on 32bit platforms - _64bit uintptr // size on 64bit platforms + val any // type as a value + _32bit uintptr // size on 32bit platforms + _64bit uintptr // size on 64bit platforms }{ // Types {Basic{}, 16, 32}, @@ -25,16 +25,14 @@ func TestSizeof(t *testing.T) { {Struct{}, 24, 48}, {Pointer{}, 8, 16}, {Tuple{}, 12, 24}, - {Signature{}, 44, 88}, - {_Sum{}, 12, 24}, - {Interface{}, 60, 120}, + {Signature{}, 28, 56}, + {Union{}, 12, 24}, + {Interface{}, 44, 88}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 64, 128}, - {_TypeParam{}, 28, 48}, - {instance{}, 44, 88}, - {bottom{}, 0, 0}, - {top{}, 0, 0}, + {Named{}, 56, 104}, + {TypeParam{}, 28, 48}, + {term{}, 12, 24}, // Objects {PkgName{}, 48, 88}, @@ -47,8 +45,9 @@ func TestSizeof(t *testing.T) { {Nil{}, 40, 72}, // Misc - {Scope{}, 40, 80}, + {Scope{}, 44, 88}, {Package{}, 40, 80}, + {_TypeSet{}, 28, 56}, } for _, test := range tests { got := reflect.TypeOf(test.val).Size() diff --git a/libgo/go/go/types/sizes.go b/libgo/go/go/types/sizes.go index 3b334ac..0dc0f4c 100644 --- a/libgo/go/go/types/sizes.go +++ b/libgo/go/go/types/sizes.go @@ -48,7 +48,7 @@ type StdSizes struct { func (s *StdSizes) Alignof(T Type) int64 { // For arrays and structs, alignment is defined in terms // of alignment of the elements and fields, respectively. - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Array: // spec: "For a variable x of array type: unsafe.Alignof(x) // is the same as unsafe.Alignof(x[0]), but at least 1." @@ -67,12 +67,17 @@ func (s *StdSizes) Alignof(T Type) int64 { case *Slice, *Interface: // Multiword data structures are effectively structs // in which each element has size WordSize. + // Type parameters lead to variable sizes/alignments; + // StdSizes.Alignof won't be called for them. + assert(!isTypeParam(T)) return s.WordSize case *Basic: // Strings are like slices and interfaces. if t.Info()&IsString != 0 { return s.WordSize } + case *TypeParam, *Union: + unreachable() } a := s.Sizeof(T) // may be 0 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." @@ -118,7 +123,7 @@ var basicSizes = [...]byte{ } func (s *StdSizes) Sizeof(T Type) int64 { - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Basic: assert(isTyped(T)) k := t.kind @@ -148,10 +153,13 @@ func (s *StdSizes) Sizeof(T Type) int64 { } offsets := s.Offsetsof(t.fields) return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) - case *_Sum: - panic("Sizeof unimplemented for type sum") case *Interface: + // Type parameters lead to variable sizes/alignments; + // StdSizes.Sizeof won't be called for them. + assert(!isTypeParam(T)) return s.WordSize * 2 + case *TypeParam, *Union: + unreachable() } return s.WordSize // catch-all } @@ -243,7 +251,7 @@ func (conf *Config) offsetsof(T *Struct) []int64 { func (conf *Config) offsetof(typ Type, index []int) int64 { var o int64 for _, i := range index { - s := asStruct(typ) + s := under(typ).(*Struct) o += conf.offsetsof(s)[i] typ = s.fields[i].typ } diff --git a/libgo/go/go/types/slice.go b/libgo/go/go/types/slice.go new file mode 100644 index 0000000..debdd81 --- /dev/null +++ b/libgo/go/go/types/slice.go @@ -0,0 +1,19 @@ +// Copyright 2011 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 types + +// A Slice represents a slice type. +type Slice struct { + elem Type +} + +// NewSlice returns a new slice type for the given element type. +func NewSlice(elem Type) *Slice { return &Slice{elem: elem} } + +// Elem returns the element type of slice s. +func (s *Slice) Elem() Type { return s.elem } + +func (t *Slice) Underlying() Type { return t } +func (t *Slice) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/stdlib_test.go b/libgo/go/go/types/stdlib_test.go index de30bdb..5c2bf55 100644 --- a/libgo/go/go/types/stdlib_test.go +++ b/libgo/go/go/types/stdlib_test.go @@ -142,8 +142,7 @@ func testTestDir(t *testing.T, path string, ignore ...string) { // parse and type-check file file, err := parser.ParseFile(fset, filename, nil, 0) if err == nil { - conf := Config{Importer: stdLibImporter} - SetGoVersion(&conf, goVersion) + conf := Config{GoVersion: goVersion, Importer: stdLibImporter} _, err = conf.Check(filename, fset, []*ast.File{file}, nil) } @@ -169,9 +168,11 @@ func TestStdTest(t *testing.T) { testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore "directive.go", // tests compiler rejection of bad directive placement - ignore + "directive2.go", // tests compiler rejection of bad directive placement - ignore "embedfunc.go", // tests //go:embed "embedvers.go", // tests //go:embed "linkname2.go", // go/types doesn't check validity of //go:xxx directives + "linkname3.go", // go/types doesn't check validity of //go:xxx directives ) } @@ -197,6 +198,10 @@ func TestStdFixed(t *testing.T) { "bug251.go", // issue #34333 which was exposed with fix for #34151 "issue42058a.go", // go/types does not have constraints on channel element size "issue42058b.go", // go/types does not have constraints on channel element size + "issue48097.go", // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function + "issue48230.go", // go/types doesn't check validity of //go:xxx directives + "issue49767.go", // go/types does not have constraints on channel element size + "issue49814.go", // go/types does not have constraints on array size ) } @@ -295,7 +300,7 @@ func pkgFilenames(dir string) ([]string, error) { return filenames, nil } -func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...interface{})) time.Duration { +func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...any)) time.Duration { w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh} w.walk(dir) return time.Since(w.start) @@ -305,7 +310,7 @@ type walker struct { start time.Time dmax time.Duration pkgh func(dir string, filenames []string) - errh func(args ...interface{}) + errh func(args ...any) } func (w *walker) walk(dir string) { diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go index 47f6dcf..8026735 100644 --- a/libgo/go/go/types/stmt.go +++ b/libgo/go/go/types/stmt.go @@ -15,7 +15,7 @@ import ( func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) { if check.conf.IgnoreFuncBodies { - panic("internal error: function body not ignored") + panic("function body not ignored") } if trace { @@ -29,13 +29,13 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body sig.scope.pos = body.Pos() sig.scope.end = body.End() - // save/restore current context and setup function context + // save/restore current environment and set up function environment // (and use 0 indentation at function start) - defer func(ctxt context, indent int) { - check.context = ctxt + defer func(env environment, indent int) { + check.environment = env check.indent = indent - }(check.context, check.indent) - check.context = context{ + }(check.environment, check.indent) + check.environment = environment{ decl: decl, scope: sig.scope, iota: iota, @@ -53,11 +53,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.error(atPos(body.Rbrace), _MissingReturn, "missing return") } - // TODO(gri) Should we make it an error to declare generic functions - // where the type parameters are not used? - // 12/19/2018: Probably not - it can make sense to have an API with - // all functions uniformly sharing the same type parameters. - // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." check.usage(sig.scope) @@ -65,7 +60,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body func (check *Checker) usage(scope *Scope) { var unused []*Var - for _, elem := range scope.elems { + for name, elem := range scope.elems { + elem = resolve(name, elem) if v, _ := elem.(*Var); v != nil && !v.used { unused = append(unused, v) } @@ -178,7 +174,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) { var x operand var msg string var code errorCode - switch check.rawExpr(&x, call, nil) { + switch check.rawExpr(&x, call, nil, false) { case conversion: msg = "requires function call, not conversion" code = _InvalidDefer @@ -197,7 +193,7 @@ func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) { } // goVal returns the Go value for val, or nil. -func goVal(val constant.Value) interface{} { +func goVal(val constant.Value) any { // val should exist, but be conservative and check if val == nil { return nil @@ -231,7 +227,7 @@ func goVal(val constant.Value) interface{} { // types we need to also check the value's types (e.g., byte(1) vs myByte(1)) // when the switch expression is of interface type. type ( - valueMap map[interface{}][]valueType // underlying Go value -> valueType + valueMap map[any][]valueType // underlying Go value -> valueType valueType struct { pos token.Pos typ Type @@ -264,7 +260,7 @@ L: // look for duplicate types for a given value // (quadratic algorithm, but these lists tend to be very short) for _, vt := range seen[val] { - if check.identical(v.typ, vt.typ) { + if Identical(v.typ, vt.typ) { check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v) check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented continue L @@ -275,24 +271,38 @@ L: } } +// isNil reports whether the expression e denotes the predeclared value nil. +func (check *Checker) isNil(e ast.Expr) bool { + // The only way to express the nil value is by literally writing nil (possibly in parentheses). + if name, _ := unparen(e).(*ast.Ident); name != nil { + _, ok := check.lookup(name.Name).(*Nil) + return ok + } + return false +} + func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) { + var dummy operand L: for _, e := range types { - T = check.typeOrNil(e) - if T == Typ[Invalid] { - continue L - } - if T != nil { - check.ordinaryType(e, T) + // The spec allows the value nil instead of a type. + if check.isNil(e) { + T = nil + check.expr(&dummy, e) // run e through expr so we get the usual Info recordings + } else { + T = check.varType(e) + if T == Typ[Invalid] { + continue L + } } // look for duplicate types // (quadratic algorithm, but type switches tend to be reasonably small) for t, other := range seen { - if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) { + if T == nil && t == nil || T != nil && t != nil && Identical(T, t) { // talk about "case" rather than "type" because of nil case Ts := "nil" if T != nil { - Ts = T.String() + Ts = TypeString(T, check.qualifier) } check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts) check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented @@ -300,13 +310,54 @@ L: } } seen[T] = e - if T != nil { + if T != nil && xtyp != nil { check.typeAssertion(e, x, xtyp, T) } } return } +// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead. +// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) +// +// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) { +// var dummy operand +// L: +// for _, e := range types { +// // The spec allows the value nil instead of a type. +// var hash string +// if check.isNil(e) { +// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings +// T = nil +// hash = "<nil>" // avoid collision with a type named nil +// } else { +// T = check.varType(e) +// if T == Typ[Invalid] { +// continue L +// } +// hash = typeHash(T, nil) +// } +// // look for duplicate types +// if other := seen[hash]; other != nil { +// // talk about "case" rather than "type" because of nil case +// Ts := "nil" +// if T != nil { +// Ts = TypeString(T, check.qualifier) +// } +// var err error_ +// err.errorf(e, "duplicate case %s in type switch", Ts) +// err.errorf(other, "previous case") +// check.report(&err) +// continue L +// } +// seen[hash] = e +// if T != nil { +// check.typeAssertion(e.Pos(), x, xtyp, T) +// } +// } +// return +// } + // stmt typechecks statement s. func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // statements must end with the same top scope as they started with @@ -340,7 +391,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // function and method calls and receive operations can appear // in statement context. Such statements may be parenthesized." var x operand - kind := check.rawExpr(&x, s.X, nil) + kind := check.rawExpr(&x, s.X, nil, false) var msg string var code errorCode switch x.mode { @@ -360,25 +411,27 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { check.errorf(&x, code, "%s %s", &x, msg) case *ast.SendStmt: - var ch, x operand + var ch, val operand check.expr(&ch, s.Chan) - check.expr(&x, s.Value) - if ch.mode == invalid || x.mode == invalid { + check.expr(&val, s.Value) + if ch.mode == invalid || val.mode == invalid { return } - - tch := asChan(ch.typ) - if tch == nil { - check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ) + u := structuralType(ch.typ) + if u == nil { + check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch) return } - - if tch.dir == RecvOnly { - check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only type %s", tch) + uch, _ := u.(*Chan) + if uch == nil { + check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch) return } - - check.assignment(&x, tch.elem, "send") + if uch.dir == RecvOnly { + check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch) + return + } + check.assignment(&val, uch.elem, "send") case *ast.IncDecStmt: var op token.Token @@ -397,7 +450,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if x.mode == invalid { return } - if !isNumeric(x.typ) { + if !allNumeric(x.typ) { check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ) return } @@ -450,27 +503,25 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { case *ast.ReturnStmt: res := check.sig.results - if res.Len() > 0 { - // function returns results - // (if one, say the first, result parameter is named, all of them are named) - if len(s.Results) == 0 && res.vars[0].name != "" { - // spec: "Implementation restriction: A compiler may disallow an empty expression - // list in a "return" statement if a different entity (constant, type, or variable) - // with the same name as a result parameter is in scope at the place of the return." - for _, obj := range res.vars { - if alt := check.lookup(obj.name); alt != nil && alt != obj { - check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name) - check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj) - // ok to continue - } + // Return with implicit results allowed for function with named results. + // (If one is named, all are named.) + if len(s.Results) == 0 && res.Len() > 0 && res.vars[0].name != "" { + // spec: "Implementation restriction: A compiler may disallow an empty expression + // list in a "return" statement if a different entity (constant, type, or variable) + // with the same name as a result parameter is in scope at the place of the return." + for _, obj := range res.vars { + if alt := check.lookup(obj.name); alt != nil && alt != obj { + check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name) + check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj) + // ok to continue } - } else { - // return has results or result parameters are unnamed - check.initVars(res.vars, s.Results, s.Return) } - } else if len(s.Results) > 0 { - check.error(s.Results[0], _WrongResultCount, "no result values expected") - check.use(s.Results...) + } else { + var lhs []*Var + if res.Len() > 0 { + lhs = res.vars + } + check.initVars(lhs, s.Results, s) } case *ast.BranchStmt: @@ -513,7 +564,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { check.simpleStmt(s.Init) var x operand check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { + if x.mode != invalid && !allBoolean(x.typ) { check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement") } check.stmt(inner, s.Body) @@ -632,12 +683,16 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if x.mode == invalid { return } - xtyp, _ := under(x.typ).(*Interface) - if xtyp == nil { - check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x) - return + // TODO(gri) we may want to permit type switches on type parameter values at some point + var xtyp *Interface + 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 { + check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x) + } } - check.ordinaryType(&x, xtyp) check.multipleDefaults(s.Body.List) @@ -746,7 +801,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if s.Cond != nil { var x operand check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { + if x.mode != invalid && !allBoolean(x.typ) { check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement") } } @@ -774,20 +829,28 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { // determine key/value types var key, val Type if x.mode != invalid { - typ := optype(x.typ) - if _, ok := typ.(*Chan); ok && s.Value != nil { - // TODO(gri) this also needs to happen for channels in generic variables - check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x) - // ok to continue + // Ranging over a type parameter is permitted if it has a structural type. + var cause string + u := structuralType(x.typ) + switch t := u.(type) { + case nil: + cause = check.sprintf("%s has no structural type", x.typ) + case *Chan: + if s.Value != nil { + check.softErrorf(s.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &x) + // ok to continue + } + if t.dir == SendOnly { + cause = "receive from send-only channel" + } } - var msg string - key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value)) - if key == nil || msg != "" { - if msg != "" { - // TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers? - msg = ": " + msg + key, val = rangeKeyVal(u) + if key == nil || cause != "" { + if cause == "" { + check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s", &x) + } else { + check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s (%s)", &x, cause) } - check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg) // ok to continue } } @@ -872,71 +935,23 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { } } -// isVarName reports whether x is a non-nil, non-blank (_) expression. -func isVarName(x ast.Expr) bool { - if x == nil { - return false - } - ident, _ := unparen(x).(*ast.Ident) - return ident == nil || ident.Name != "_" -} - // rangeKeyVal returns the key and value type produced by a range clause -// over an expression of type typ, and possibly an error message. If the -// range clause is not permitted the returned key is nil or msg is not -// empty (in that case we still may have a non-nil key type which can be -// used to reduce the chance for follow-on errors). -// The wantKey, wantVal, and hasVal flags indicate which of the iteration -// variables are used or present; this matters if we range over a generic -// type where not all keys or values are of the same type. -func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) { - switch typ := typ.(type) { +// over an expression of type typ. If the range clause is not permitted +// the results are nil. +func rangeKeyVal(typ Type) (key, val Type) { + switch typ := arrayPtrDeref(typ).(type) { case *Basic: if isString(typ) { - return Typ[Int], universeRune, "" // use 'rune' name + return Typ[Int], universeRune // use 'rune' name } case *Array: - return Typ[Int], typ.elem, "" + return Typ[Int], typ.elem case *Slice: - return Typ[Int], typ.elem, "" - case *Pointer: - if typ := asArray(typ.base); typ != nil { - return Typ[Int], typ.elem, "" - } + return Typ[Int], typ.elem case *Map: - return typ.key, typ.elem, "" + return typ.key, typ.elem case *Chan: - var msg string - if typ.dir == SendOnly { - msg = "send-only channel" - } - return typ.elem, Typ[Invalid], msg - case *_Sum: - first := true - var key, val Type - var msg string - typ.is(func(t Type) bool { - k, v, m := rangeKeyVal(under(t), wantKey, wantVal) - if k == nil || m != "" { - key, val, msg = k, v, m - return false - } - if first { - key, val, msg = k, v, m - first = false - return true - } - if wantKey && !Identical(key, k) { - key, val, msg = nil, nil, "all possible values must have the same key type" - return false - } - if wantVal && !Identical(val, v) { - key, val, msg = nil, nil, "all possible values must have the same element type" - return false - } - return true - }) - return key, val, msg + return typ.elem, Typ[Invalid] } - return nil, nil, "" + return } diff --git a/libgo/go/go/types/struct.go b/libgo/go/go/types/struct.go new file mode 100644 index 0000000..d6c5634 --- /dev/null +++ b/libgo/go/go/types/struct.go @@ -0,0 +1,217 @@ +// 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 types + +import ( + "go/ast" + "go/token" + "strconv" +) + +// ---------------------------------------------------------------------------- +// API + +// A Struct represents a struct type. +type Struct struct { + fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0) + tags []string // field tags; nil if there are no tags +} + +// NewStruct returns a new struct with the given fields and corresponding field tags. +// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be +// only as long as required to hold the tag with the largest index i. Consequently, +// if no field has a tag, tags may be nil. +func NewStruct(fields []*Var, tags []string) *Struct { + var fset objset + for _, f := range fields { + if f.name != "_" && fset.insert(f) != nil { + panic("multiple fields with the same name") + } + } + if len(tags) > len(fields) { + panic("more tags than fields") + } + s := &Struct{fields: fields, tags: tags} + s.markComplete() + return s +} + +// NumFields returns the number of fields in the struct (including blank and embedded fields). +func (s *Struct) NumFields() int { return len(s.fields) } + +// Field returns the i'th field for 0 <= i < NumFields(). +func (s *Struct) Field(i int) *Var { return s.fields[i] } + +// Tag returns the i'th field tag for 0 <= i < NumFields(). +func (s *Struct) Tag(i int) string { + if i < len(s.tags) { + return s.tags[i] + } + return "" +} + +func (t *Struct) Underlying() Type { return t } +func (t *Struct) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +func (s *Struct) markComplete() { + if s.fields == nil { + s.fields = make([]*Var, 0) + } +} + +func (check *Checker) structType(styp *Struct, e *ast.StructType) { + list := e.Fields + if list == nil { + styp.markComplete() + return + } + + // struct fields and tags + var fields []*Var + var tags []string + + // for double-declaration checks + var fset objset + + // current field typ and tag + var typ Type + var tag string + add := func(ident *ast.Ident, embedded bool, pos token.Pos) { + if tag != "" && tags == nil { + tags = make([]string, len(fields)) + } + if tags != nil { + tags = append(tags, tag) + } + + name := ident.Name + fld := NewField(pos, check.pkg, name, typ, embedded) + // spec: "Within a struct, non-blank field names must be unique." + if name == "_" || check.declareInSet(&fset, pos, fld) { + fields = append(fields, fld) + check.recordDef(ident, fld) + } + } + + // addInvalid adds an embedded field of invalid type to the struct for + // fields with errors; this keeps the number of struct fields in sync + // with the source as long as the fields are _ or have different names + // (issue #25627). + addInvalid := func(ident *ast.Ident, pos token.Pos) { + typ = Typ[Invalid] + tag = "" + add(ident, true, pos) + } + + for _, f := range list.List { + typ = check.varType(f.Type) + tag = check.tag(f.Tag) + if len(f.Names) > 0 { + // named fields + for _, name := range f.Names { + add(name, false, name.Pos()) + } + } else { + // embedded field + // spec: "An embedded type must be specified as a type name T or as a + // pointer to a non-interface type name *T, and T itself may not be a + // pointer type." + pos := f.Type.Pos() + name := embeddedFieldIdent(f.Type) + if name == nil { + check.invalidAST(f.Type, "embedded field type %s has no name", f.Type) + name = ast.NewIdent("_") + name.NamePos = pos + addInvalid(name, pos) + continue + } + add(name, true, pos) + + // Because we have a name, typ must be of the form T or *T, where T is the name + // of a (named or alias) type, and t (= deref(typ)) must be the type of T. + // We must delay this check to the end because we don't want to instantiate + // (via under(t)) a possibly incomplete type. + + // for use in the closure below + embeddedTyp := typ + embeddedPos := f.Type + + check.later(func() { + t, isPtr := deref(embeddedTyp) + switch u := under(t).(type) { + case *Basic: + if t == Typ[Invalid] { + // error was reported before + return + } + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer") + } + case *Pointer: + check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer") + case *Interface: + if isTypeParam(t) { + // The error code here is inconsistent with other error codes for + // invalid embedding, because this restriction may be relaxed in the + // future, and so it did not warrant a new error code. + check.error(embeddedPos, _MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter") + break + } + if isPtr { + check.error(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface") + } + } + }).describef(embeddedPos, "check embedded type %s", embeddedTyp) + } + } + + styp.fields = fields + styp.tags = tags + styp.markComplete() +} + +func embeddedFieldIdent(e ast.Expr) *ast.Ident { + switch e := e.(type) { + case *ast.Ident: + return e + case *ast.StarExpr: + // *T is valid, but **T is not + if _, ok := e.X.(*ast.StarExpr); !ok { + return embeddedFieldIdent(e.X) + } + case *ast.SelectorExpr: + return e.Sel + case *ast.IndexExpr: + return embeddedFieldIdent(e.X) + case *ast.IndexListExpr: + return embeddedFieldIdent(e.X) + } + return nil // invalid embedded field +} + +func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool { + if alt := oset.insert(obj); alt != nil { + check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name()) + check.reportAltDecl(alt) + return false + } + return true +} + +func (check *Checker) tag(t *ast.BasicLit) string { + if t != nil { + if t.Kind == token.STRING { + if val, err := strconv.Unquote(t.Value); err == nil { + return val + } + } + check.invalidAST(t, "incorrect tag syntax: %q", t.Value) + } + return "" +} diff --git a/libgo/go/go/types/subst.go b/libgo/go/go/types/subst.go index 931375f..0cce46a 100644 --- a/libgo/go/go/types/subst.go +++ b/libgo/go/go/types/subst.go @@ -2,218 +2,43 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements instantiation of generic types -// through substitution of type parameters by actual -// types. +// This file implements type parameter substitution. package types -import ( - "bytes" - "fmt" - "go/token" -) - -// TODO(rFindley) decide error codes for the errors in this file, and check -// if error spans can be improved - -type substMap struct { - // The targs field is currently needed for *Named type substitution. - // TODO(gri) rewrite that code, get rid of this field, and make this - // struct just the map (proj) - targs []Type - proj map[*_TypeParam]Type -} +import "go/token" + +type substMap map[*TypeParam]Type // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. // If targs[i] is nil, tpars[i] is not substituted. -func makeSubstMap(tpars []*TypeName, targs []Type) *substMap { +func makeSubstMap(tpars []*TypeParam, targs []Type) substMap { assert(len(tpars) == len(targs)) - proj := make(map[*_TypeParam]Type, len(tpars)) + proj := make(substMap, len(tpars)) for i, tpar := range tpars { - // We must expand type arguments otherwise *instance - // types end up as components in composite types. - // TODO(gri) explain why this causes problems, if it does - targ := expand(targs[i]) // possibly nil - targs[i] = targ - proj[tpar.typ.(*_TypeParam)] = targ + proj[tpar] = targs[i] } - return &substMap{targs, proj} -} - -func (m *substMap) String() string { - return fmt.Sprintf("%s", m.proj) + return proj } -func (m *substMap) empty() bool { - return len(m.proj) == 0 +func (m substMap) empty() bool { + return len(m) == 0 } -func (m *substMap) lookup(tpar *_TypeParam) Type { - if t := m.proj[tpar]; t != nil { +func (m substMap) lookup(tpar *TypeParam) Type { + if t := m[tpar]; t != nil { return t } return tpar } -func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) { - if trace { - check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs)) - check.indent++ - defer func() { - check.indent-- - var under Type - if res != nil { - // Calling under() here may lead to endless instantiations. - // Test case: type T[P any] T[P] - // TODO(gri) investigate if that's a bug or to be expected. - under = res.Underlying() - } - check.trace(pos, "=> %s (under = %s)", res, under) - }() - } - - assert(len(poslist) <= len(targs)) - - // TODO(gri) What is better here: work with TypeParams, or work with TypeNames? - var tparams []*TypeName - switch t := typ.(type) { - case *Named: - tparams = t.tparams - case *Signature: - tparams = t.tparams - defer func() { - // If we had an unexpected failure somewhere don't panic below when - // asserting res.(*Signature). Check for *Signature in case Typ[Invalid] - // is returned. - if _, ok := res.(*Signature); !ok { - return - } - // If the signature doesn't use its type parameters, subst - // will not make a copy. In that case, make a copy now (so - // we can set tparams to nil w/o causing side-effects). - if t == res { - copy := *t - res = © - } - // After instantiating a generic signature, it is not generic - // anymore; we need to set tparams to nil. - res.(*Signature).tparams = nil - }() - - default: - check.dump("%v: cannot instantiate %v", pos, typ) - unreachable() // only defined types and (defined) functions can be generic - } - - // the number of supplied types must match the number of type parameters - if len(targs) != len(tparams) { - // TODO(gri) provide better error message - check.errorf(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), len(tparams)) - return Typ[Invalid] - } - - if len(tparams) == 0 { - return typ // nothing to do (minor optimization) - } - - smap := makeSubstMap(tparams, targs) - - // check bounds - for i, tname := range tparams { - tpar := tname.typ.(*_TypeParam) - iface := tpar.Bound() - if iface.Empty() { - continue // no type bound - } - - targ := targs[i] - - // best position for error reporting - pos := pos - if i < len(poslist) { - pos = poslist[i] - } - - // 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 instantiate - // the parameterized type. - iface = check.subst(pos, iface, smap).(*Interface) - - // targ must implement iface (methods) - // - check only if we have methods - check.completeInterface(token.NoPos, iface) - if len(iface.allMethods) > 0 { - // If the type argument is a pointer to a type parameter, the type argument's - // method set is empty. - // TODO(gri) is this what we want? (spec question) - if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil { - check.errorf(atPos(pos), 0, "%s has no methods", targ) - break - } - if m, wrong := check.missingMethod(targ, iface, true); m != nil { - // TODO(gri) needs to print updated name to avoid major confusion in error message! - // (print warning for now) - // Old warning: - // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m) - if m.name == "==" { - // We don't want to report "missing method ==". - check.softErrorf(atPos(pos), 0, "%s does not satisfy comparable", targ) - } else if wrong != nil { - // TODO(gri) This can still report uninstantiated types which makes the error message - // more difficult to read then necessary. - // TODO(rFindley) should this use parentheses rather than ':' for qualification? - check.softErrorf(atPos(pos), _Todo, - "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s", - targ, tpar.bound, wrong, m, - ) - } else { - check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name) - } - break - } - } - - // targ's underlying type must also be one of the interface types listed, if any - if iface.allTypes == nil { - continue // nothing to do - } - - // If targ is itself a type parameter, each of its possible types, but at least one, must be in the - // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types). - if targ := asTypeParam(targ); targ != nil { - targBound := targ.Bound() - if targBound.allTypes == nil { - check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ) - break - } - for _, t := range unpackType(targBound.allTypes) { - if !iface.isSatisfiedBy(t) { - // TODO(gri) match this error message with the one below (or vice versa) - check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes) - break - } - } - break - } - - // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any. - if !iface.isSatisfiedBy(targ) { - check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes) - break - } - } - - return check.subst(pos, typ, smap) -} - -// subst returns the type typ with its type parameters tpars replaced by -// the corresponding type arguments targs, recursively. -// subst is functional in the sense that it doesn't modify the incoming -// type. If a substitution took place, the result type is different from -// from the incoming type. -func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type { +// subst returns the type typ with its type parameters tpars replaced by the +// corresponding type arguments targs, recursively. subst is pure in the sense +// that it doesn't modify the incoming type. If a substitution took place, the +// result type is different from the incoming type. +// +// If the given context is non-nil, it is used in lieu of check.Config.Context +func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, ctxt *Context) Type { if smap.empty() { return typ } @@ -222,20 +47,25 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type { switch t := typ.(type) { case *Basic: return typ // nothing to do - case *_TypeParam: + case *TypeParam: return smap.lookup(t) } // general case - subst := subster{check, pos, make(map[Type]Type), smap} + subst := subster{ + pos: pos, + smap: smap, + check: check, + ctxt: check.bestContext(ctxt), + } return subst.typ(typ) } type subster struct { - check *Checker pos token.Pos - cache map[Type]Type - smap *substMap + smap substMap + check *Checker // nil if called via Instantiate + ctxt *Context } func (subst *subster) typ(typ Type) Type { @@ -244,7 +74,7 @@ func (subst *subster) typ(typ Type) Type { // Call typOrNil if it's possible that typ is nil. panic("nil typ") - case *Basic, *bottom, *top: + case *Basic: // nothing to do case *Array: @@ -261,7 +91,9 @@ func (subst *subster) typ(typ Type) Type { case *Struct: if fields, copied := subst.varList(t.fields); copied { - return &Struct{fields: fields, tags: t.tags} + s := &Struct{fields: fields, tags: t.tags} + s.markComplete() + return s } case *Pointer: @@ -274,18 +106,29 @@ func (subst *subster) typ(typ Type) Type { return subst.tuple(t) case *Signature: - // TODO(gri) rethink the recv situation with respect to methods on parameterized types - // recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain + // Preserve the receiver: it is handled during *Interface and *Named type + // substitution. + // + // Naively doing the substitution here can lead to an infinite recursion in + // the case where the receiver is an interface. For example, consider the + // following declaration: + // + // type T[A any] struct { f interface{ m() } } + // + // In this case, the type of f is an interface that is itself the receiver + // type of all of its methods. Because we have no type name to break + // cycles, substituting in the recv results in an infinite loop of + // recv->interface->recv->interface->... recv := t.recv + params := subst.tuple(t.params) results := subst.tuple(t.results) - if recv != t.recv || params != t.params || results != t.results { + if params != t.params || results != t.results { return &Signature{ rparams: t.rparams, - // TODO(rFindley) why can't we nil out tparams here, rather than in - // instantiate above? - tparams: t.tparams, - scope: t.scope, + // TODO(rFindley) why can't we nil out tparams here, rather than in instantiate? + tparams: t.tparams, + // instantiated signatures have a nil scope recv: recv, params: params, results: results, @@ -293,26 +136,34 @@ func (subst *subster) typ(typ Type) Type { } } - case *_Sum: - types, copied := subst.typeList(t.types) + case *Union: + terms, copied := subst.termlist(t.terms) if copied { - // Don't do it manually, with a Sum literal: the new - // types list may not be unique and NewSum may remove - // duplicates. - return _NewSum(types) + // term list substitution may introduce duplicate terms (unlikely but possible). + // This is ok; lazy type set computation will determine the actual type set + // in normal form. + return &Union{terms} } case *Interface: methods, mcopied := subst.funcList(t.methods) - types := t.types - if t.types != nil { - types = subst.typ(t.types) - } embeddeds, ecopied := subst.typeList(t.embeddeds) - if mcopied || types != t.types || ecopied { - iface := &Interface{methods: methods, types: types, embeddeds: embeddeds} - subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement - subst.check.completeInterface(token.NoPos, iface) + if mcopied || ecopied { + iface := &Interface{embeddeds: embeddeds, implicit: t.implicit, complete: t.complete} + // If we've changed the interface type, we may need to replace its + // receiver if the receiver type is the original interface. Receivers of + // *Named type are replaced during named type expansion. + // + // Notably, it's possible to reach here and not create a new *Interface, + // even though the receiver type may be parameterized. For example: + // + // type T[P any] interface{ m() } + // + // In this case the interface will not be substituted here, because its + // method signatures do not depend on the type parameter P, but we still + // need to create new interface methods to hold the instantiated + // receiver. This is handled by expandNamed. + iface.methods, _ = replaceRecvType(methods, t, iface) return iface } @@ -330,85 +181,81 @@ func (subst *subster) typ(typ Type) Type { } case *Named: - subst.check.indent++ - defer func() { - subst.check.indent-- - }() - dump := func(format string, args ...interface{}) { - if trace { + // dump is for debugging + dump := func(string, ...any) {} + if subst.check != nil && trace { + subst.check.indent++ + defer func() { + subst.check.indent-- + }() + dump = func(format string, args ...any) { subst.check.trace(subst.pos, format, args...) } } - if t.tparams == nil { + // subst is called by expandNamed, so in this function we need to be + // careful not to call any methods that would cause t to be expanded: doing + // so would result in deadlock. + // + // So we call t.orig.TypeParams() rather than t.TypeParams() here and + // below. + if t.orig.TypeParams().Len() == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } - var newTargs []Type - - if len(t.targs) > 0 { - // already instantiated - dump(">>> %s already instantiated", t) - assert(len(t.targs) == len(t.tparams)) - // For each (existing) type argument targ, determine if it needs - // to be substituted; i.e., if it is or contains a type parameter - // that has a type argument for it. - for i, targ := range t.targs { - dump(">>> %d targ = %s", i, targ) - newTarg := subst.typ(targ) - if newTarg != targ { - dump(">>> substituted %d targ %s => %s", i, targ, newTarg) - if newTargs == nil { - newTargs = make([]Type, len(t.tparams)) - copy(newTargs, t.targs) - } - newTargs[i] = newTarg + var newTArgs []Type + if t.targs.Len() != t.orig.TypeParams().Len() { + return Typ[Invalid] // error reported elsewhere + } + + // already instantiated + dump(">>> %s already instantiated", t) + // For each (existing) type argument targ, determine if it needs + // to be substituted; i.e., if it is or contains a type parameter + // that has a type argument for it. + for i, targ := range t.targs.list() { + dump(">>> %d targ = %s", i, targ) + new_targ := subst.typ(targ) + if new_targ != targ { + dump(">>> substituted %d targ %s => %s", i, targ, new_targ) + if newTArgs == nil { + newTArgs = make([]Type, t.orig.TypeParams().Len()) + copy(newTArgs, t.targs.list()) } + newTArgs[i] = new_targ } + } - if newTargs == nil { - dump(">>> nothing to substitute in %s", t) - return t // nothing to substitute - } - } else { - // not yet instantiated - dump(">>> first instantiation of %s", t) - // TODO(rFindley) can we instead subst the tparam types here? - newTargs = subst.smap.targs + if newTArgs == nil { + dump(">>> nothing to substitute in %s", t) + return t // nothing to substitute } // before creating a new named type, check if we have this one already - h := instantiatedHash(t, newTargs) + h := subst.ctxt.instanceHash(t.orig, newTArgs) dump(">>> new type hash: %s", h) - if named, found := subst.check.typMap[h]; found { + if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil { dump(">>> found %s", named) - subst.cache[t] = named return named } - // create a new named type and populate caches to avoid endless recursion - tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) - named := subst.check.newNamed(tname, t.underlying, t.methods) // method signatures are updated lazily - named.tparams = t.tparams // new type is still parameterized - named.targs = newTargs - subst.check.typMap[h] = named - subst.cache[t] = named - - // do the substitution - dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs) - named.underlying = subst.typOrNil(t.underlying) - named.orig = named.underlying // for cycle detection (Checker.validType) + // Create a new instance and populate the context to avoid endless + // recursion. The position used here is irrelevant because validation only + // occurs on t (we don't call validType on named), but we use subst.pos to + // help with debugging. + t.orig.resolve(subst.ctxt) + return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt) - return named + // Note that if we were to expose substitution more generally (not just in + // the context of a declaration), we'd have to substitute in + // named.underlying as well. + // + // But this is unnecessary for now. - case *_TypeParam: + case *TypeParam: return subst.smap.lookup(t) - case *instance: - // TODO(gri) can we avoid the expansion here and just substitute the type parameters? - return subst.typ(t.expand()) - default: panic("unimplemented") } @@ -416,37 +263,6 @@ func (subst *subster) typ(typ Type) Type { return typ } -// TODO(gri) Eventually, this should be more sophisticated. -// It won't work correctly for locally declared types. -func instantiatedHash(typ *Named, targs []Type) string { - var buf bytes.Buffer - writeTypeName(&buf, typ.obj, nil) - buf.WriteByte('[') - writeTypeList(&buf, targs, nil, nil) - buf.WriteByte(']') - - // With respect to the represented type, whether a - // type is fully expanded or stored as instance - // does not matter - they are the same types. - // Remove the instanceMarkers printed for instances. - res := buf.Bytes() - i := 0 - for _, b := range res { - if b != instanceMarker { - res[i] = b - i++ - } - } - - return string(res[:i]) -} - -func typeListString(list []Type) string { - var buf bytes.Buffer - writeTypeList(&buf, list, nil, nil) - return buf.String() -} - // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) // where an array/slice element is accessed before it is set up. @@ -541,3 +357,49 @@ func (subst *subster) typeList(in []Type) (out []Type, copied bool) { } return } + +func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) { + out = in + for i, t := range in { + if u := subst.typ(t.typ); u != t.typ { + if !copied { + // first function that got substituted => allocate new out slice + // and copy all functions + new := make([]*Term, len(in)) + copy(new, out) + out = new + copied = true + } + out[i] = NewTerm(t.tilde, u) + } + } + return +} + +// replaceRecvType updates any function receivers that have type old to have +// type new. It does not modify the input slice; if modifications are required, +// the input slice and any affected signatures will be copied before mutating. +// +// The resulting out slice contains the updated functions, and copied reports +// if anything was modified. +func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { + out = in + for i, method := range in { + sig := method.Type().(*Signature) + if sig.recv != nil && sig.recv.Type() == old { + if !copied { + // Allocate a new methods slice before mutating for the first time. + // This is defensive, as we may share methods across instantiations of + // a given interface type if they do not get substituted. + out = make([]*Func, len(in)) + copy(out, in) + copied = true + } + newsig := *sig + sig = &newsig + sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new) + out[i] = NewFunc(method.pos, method.pkg, method.name, sig) + } + } + return +} diff --git a/libgo/go/go/types/termlist.go b/libgo/go/go/types/termlist.go new file mode 100644 index 0000000..c4ab0e0 --- /dev/null +++ b/libgo/go/go/types/termlist.go @@ -0,0 +1,167 @@ +// 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 types + +import "bytes" + +// A termlist represents the type set represented by the union +// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "∅" + } + var buf bytes.Buffer + for i, x := range xl { + if i > 0 { + buf.WriteString(" ∪ ") + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓤 term, the entire list is 𝓤. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓤 term, the entire list is 𝓤. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓤 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// If the type set represented by xl is specified by a single (non-𝓤) term, +// singleType returns that type. Otherwise it returns nil. +func (xl termlist) singleType() Type { + if nl := xl.norm(); len(nl) == 1 { + return nl[0].typ // if nl.isAll() then typ is nil, which is ok + } + return nil +} + +// union returns the union xl ∪ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y ⊆ xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl ⊆ yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/libgo/go/go/types/termlist_test.go b/libgo/go/go/types/termlist_test.go new file mode 100644 index 0000000..dddca7a --- /dev/null +++ b/libgo/go/go/types/termlist_test.go @@ -0,0 +1,313 @@ +// 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 types + +import ( + "strings" + "testing" +) + +// maketl makes a term list from a string of the term list. +func maketl(s string) termlist { + s = strings.ReplaceAll(s, " ", "") + names := strings.Split(s, "∪") + r := make(termlist, len(names)) + for i, n := range names { + r[i] = testTerm(n) + } + return r +} + +func TestTermlistAll(t *testing.T) { + if !allTermlist.isAll() { + t.Errorf("allTermlist is not the set of all types") + } +} + +func TestTermlistString(t *testing.T) { + for _, want := range []string{ + "∅", + "𝓤", + "int", + "~int", + "myInt", + "∅ ∪ ∅", + "𝓤 ∪ 𝓤", + "∅ ∪ 𝓤 ∪ int", + "∅ ∪ 𝓤 ∪ int ∪ myInt", + } { + if got := maketl(want).String(); got != want { + t.Errorf("(%v).String() == %v", want, got) + } + } +} + +func TestTermlistIsEmpty(t *testing.T) { + for test, want := range map[string]bool{ + "∅": true, + "∅ ∪ ∅": true, + "∅ ∪ ∅ ∪ 𝓤": false, + "∅ ∪ ∅ ∪ myInt": false, + "𝓤": false, + "𝓤 ∪ int": false, + "𝓤 ∪ myInt ∪ ∅": false, + } { + xl := maketl(test) + got := xl.isEmpty() + if got != want { + t.Errorf("(%v).isEmpty() == %v; want %v", test, got, want) + } + } +} + +func TestTermlistIsAll(t *testing.T) { + for test, want := range map[string]bool{ + "∅": false, + "∅ ∪ ∅": false, + "int ∪ ~string": false, + "~int ∪ myInt": false, + "∅ ∪ ∅ ∪ 𝓤": true, + "𝓤": true, + "𝓤 ∪ int": true, + "myInt ∪ 𝓤": true, + } { + xl := maketl(test) + got := xl.isAll() + if got != want { + t.Errorf("(%v).isAll() == %v; want %v", test, got, want) + } + } +} + +func TestTermlistNorm(t *testing.T) { + for _, test := range []struct { + xl, want string + }{ + {"∅", "∅"}, + {"∅ ∪ ∅", "∅"}, + {"∅ ∪ int", "int"}, + {"∅ ∪ myInt", "myInt"}, + {"𝓤 ∪ int", "𝓤"}, + {"𝓤 ∪ myInt", "𝓤"}, + {"int ∪ myInt", "int ∪ myInt"}, + {"~int ∪ int", "~int"}, + {"~int ∪ myInt", "~int"}, + {"int ∪ ~string ∪ int", "int ∪ ~string"}, + {"~int ∪ string ∪ 𝓤 ∪ ~string ∪ int", "𝓤"}, + {"~int ∪ string ∪ myInt ∪ ~string ∪ int", "~int ∪ ~string"}, + } { + xl := maketl(test.xl) + got := maketl(test.xl).norm() + if got.String() != test.want { + t.Errorf("(%v).norm() = %v; want %v", xl, got, test.want) + } + } +} + +func TestTermlistSingleType(t *testing.T) { + // helper to deal with nil types + tstring := func(typ Type) string { + if typ == nil { + return "nil" + } + return typ.String() + } + + for test, want := range map[string]string{ + "∅": "nil", + "𝓤": "nil", + "int": "int", + "myInt": "myInt", + "~int": "int", + "~int ∪ string": "nil", + "~int ∪ myInt": "int", + "∅ ∪ int": "int", + "∅ ∪ ~int": "int", + "∅ ∪ ~int ∪ string": "nil", + } { + xl := maketl(test) + got := tstring(xl.singleType()) + if got != want { + t.Errorf("(%v).singleType() == %v; want %v", test, got, want) + } + } +} + +func TestTermlistUnion(t *testing.T) { + for _, test := range []struct { + xl, yl, want string + }{ + + {"∅", "∅", "∅"}, + {"∅", "𝓤", "𝓤"}, + {"∅", "int", "int"}, + {"𝓤", "~int", "𝓤"}, + {"int", "~int", "~int"}, + {"int", "string", "int ∪ string"}, + {"int", "myInt", "int ∪ myInt"}, + {"~int", "myInt", "~int"}, + {"int ∪ string", "~string", "int ∪ ~string"}, + {"~int ∪ string", "~string ∪ int", "~int ∪ ~string"}, + {"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, + {"~int ∪ myInt ∪ ∅", "~string ∪ int", "~int ∪ ~string"}, + {"~int ∪ string ∪ 𝓤", "~string ∪ int", "𝓤"}, + {"~int ∪ string ∪ myInt", "~string ∪ int", "~int ∪ ~string"}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.union(yl).String() + if got != test.want { + t.Errorf("(%v).union(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistIntersect(t *testing.T) { + for _, test := range []struct { + xl, yl, want string + }{ + + {"∅", "∅", "∅"}, + {"∅", "𝓤", "∅"}, + {"∅", "int", "∅"}, + {"∅", "myInt", "∅"}, + {"𝓤", "~int", "~int"}, + {"𝓤", "myInt", "myInt"}, + {"int", "~int", "int"}, + {"int", "string", "∅"}, + {"int", "myInt", "∅"}, + {"~int", "myInt", "myInt"}, + {"int ∪ string", "~string", "string"}, + {"~int ∪ string", "~string ∪ int", "int ∪ string"}, + {"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"}, + {"~int ∪ myInt ∪ ∅", "~string ∪ int", "int"}, + {"~int ∪ string ∪ 𝓤", "~string ∪ int", "int ∪ ~string"}, + {"~int ∪ string ∪ myInt", "~string ∪ int", "int ∪ string"}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.intersect(yl).String() + if got != test.want { + t.Errorf("(%v).intersect(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistEqual(t *testing.T) { + for _, test := range []struct { + xl, yl string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", false}, + {"𝓤", "𝓤", true}, + {"𝓤 ∪ int", "𝓤", true}, + {"𝓤 ∪ int", "string ∪ 𝓤", true}, + {"𝓤 ∪ myInt", "string ∪ 𝓤", true}, + {"int ∪ ~string", "string ∪ int", false}, + {"~int ∪ string", "string ∪ myInt", false}, + {"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.equal(yl) + if got != test.want { + t.Errorf("(%v).equal(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistIncludes(t *testing.T) { + for _, test := range []struct { + xl, typ string + want bool + }{ + {"∅", "int", false}, + {"𝓤", "int", true}, + {"~int", "int", true}, + {"int", "string", false}, + {"~int", "string", false}, + {"~int", "myInt", true}, + {"int ∪ string", "string", true}, + {"~int ∪ string", "int", true}, + {"~int ∪ string", "myInt", true}, + {"~int ∪ myInt ∪ ∅", "myInt", true}, + {"myInt ∪ ∅ ∪ 𝓤", "int", true}, + } { + xl := maketl(test.xl) + yl := testTerm(test.typ).typ + got := xl.includes(yl) + if got != test.want { + t.Errorf("(%v).includes(%v) = %v; want %v", test.xl, yl, got, test.want) + } + } +} + +func TestTermlistSupersetOf(t *testing.T) { + for _, test := range []struct { + xl, typ string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", false}, + {"∅", "int", false}, + {"𝓤", "∅", true}, + {"𝓤", "𝓤", true}, + {"𝓤", "int", true}, + {"𝓤", "~int", true}, + {"𝓤", "myInt", true}, + {"~int", "int", true}, + {"~int", "~int", true}, + {"~int", "myInt", true}, + {"int", "~int", false}, + {"myInt", "~int", false}, + {"int", "string", false}, + {"~int", "string", false}, + {"int ∪ string", "string", true}, + {"int ∪ string", "~string", false}, + {"~int ∪ string", "int", true}, + {"~int ∪ string", "myInt", true}, + {"~int ∪ string ∪ ∅", "string", true}, + {"~string ∪ ∅ ∪ 𝓤", "myInt", true}, + } { + xl := maketl(test.xl) + y := testTerm(test.typ) + got := xl.supersetOf(y) + if got != test.want { + t.Errorf("(%v).supersetOf(%v) = %v; want %v", test.xl, y, got, test.want) + } + } +} + +func TestTermlistSubsetOf(t *testing.T) { + for _, test := range []struct { + xl, yl string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", true}, + {"𝓤", "∅", false}, + {"𝓤", "𝓤", true}, + {"int", "int ∪ string", true}, + {"~int", "int ∪ string", false}, + {"~int", "myInt ∪ string", false}, + {"myInt", "~int ∪ string", true}, + {"~int", "string ∪ string ∪ int ∪ ~int", true}, + {"myInt", "string ∪ string ∪ ~int", true}, + {"int ∪ string", "string", false}, + {"int ∪ string", "string ∪ int", true}, + {"int ∪ ~string", "string ∪ int", false}, + {"myInt ∪ ~string", "string ∪ int ∪ 𝓤", true}, + {"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false}, + {"int ∪ myInt", "string ∪ ~int ∪ ∅ ∪ string", true}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.subsetOf(yl) + if got != test.want { + t.Errorf("(%v).subsetOf(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} diff --git a/libgo/go/go/types/testdata/check/builtins.go2 b/libgo/go/go/types/testdata/check/builtins.go2 index 3918d83..c1accff 100644 --- a/libgo/go/go/types/testdata/check/builtins.go2 +++ b/libgo/go/go/types/testdata/check/builtins.go2 @@ -6,48 +6,269 @@ package builtins -type Bmc interface { - type map[rune]string, chan int +import "unsafe" + +// close + +type C0 interface{ int } +type C1 interface{ chan int } +type C2 interface{ chan int | <-chan int } +type C3 interface{ chan int | chan float32 } +type C4 interface{ chan int | chan<- int } +type C5[T any] interface{ ~chan T | chan<- T } + +func _[T any](ch T) { + close(ch /* ERROR cannot close non-channel */) +} + +func _[T C0](ch T) { + close(ch /* ERROR cannot close non-channel */) +} + +func _[T C1](ch T) { + close(ch) +} + +func _[T C2](ch T) { + close(ch /* ERROR cannot close receive-only channel */) +} + +func _[T C3](ch T) { + close(ch) +} + +func _[T C4](ch T) { + close(ch) } -type Bms interface { - type map[string]int, []int +func _[T C5[X], X any](ch T) { + close(ch) } -type Bcs interface { - type chan bool, []float64 +// copy + +func _[T any](x, y T) { + copy(x /* ERROR copy expects slice arguments */ , y) } -type Bss interface { - type []int, []string +func _[T ~[]byte](x, y T) { + copy(x, y) + copy(x, "foo") + copy("foo" /* ERROR expects slice arguments */ , y) + + var x2 []byte + copy(x2, y) // element types are identical + copy(y, x2) // element types are identical + + type myByte byte + var x3 []myByte + copy(x3 /* ERROR different element types */ , y) + copy(y /* ERROR different element types */ , x3) } -func _[T any] () { - _ = make(T /* ERROR invalid argument */ ) - _ = make(T /* ERROR invalid argument */ , 10) - _ = make(T /* ERROR invalid argument */ , 10, 20) +func _[T ~[]E, E any](x T, y []E) { + copy(x, y) + copy(x /* ERROR different element types */ , "foo") } -func _[T Bmc] () { - _ = make(T) - _ = make(T, 10) - _ = make /* ERROR expects 1 or 2 arguments */ (T, 10, 20) +func _[T ~string](x []byte, y T) { + copy(x, y) + copy(y /* ERROR expects slice arguments */ , x) } -func _[T Bms] () { - _ = make /* ERROR expects 2 arguments */ (T) - _ = make(T, 10) - _ = make /* ERROR expects 2 arguments */ (T, 10, 20) +func _[T ~[]byte|~string](x T, y []byte) { + copy(x /* ERROR expects slice arguments */ , y) + copy(y, x) } -func _[T Bcs] () { - _ = make /* ERROR expects 2 arguments */ (T) - _ = make(T, 10) - _ = make /* ERROR expects 2 arguments */ (T, 10, 20) +type L0 []int +type L1 []int + +func _[T L0 | L1](x, y T) { + copy(x, y) } -func _[T Bss] () { - _ = make /* ERROR expects 2 or 3 arguments */ (T) - _ = make(T, 10) - _ = make(T, 10, 20) +// delete + +type M0 interface{ int } +type M1 interface{ map[string]int } +type M2 interface { map[string]int | map[string]float64 } +type M3 interface{ map[string]int | map[rune]int } +type M4[K comparable, V any] interface{ map[K]V | map[rune]V } + +func _[T any](m T) { + delete(m /* ERROR not a map */, "foo") +} + +func _[T M0](m T) { + delete(m /* ERROR not a map */, "foo") +} + +func _[T M1](m T) { + delete(m, "foo") +} + +func _[T M2](m T) { + delete(m, "foo") + delete(m, 0 /* ERROR cannot use .* as string */) +} + +func _[T M3](m T) { + delete(m /* ERROR must have identical key types */, "foo") +} + +func _[T M4[rune, V], V any](m T) { + delete(m, 'k') +} + +func _[T M4[K, V], K comparable, V any](m T) { + delete(m /* ERROR must have identical key types */, "foo") +} + +// make + +type myChan chan int + +func _[ + S1 ~[]int, + S2 ~[]int | ~chan int, + + M1 ~map[string]int, + M2 ~map[string]int | ~chan int, + + C1 ~chan int, + C2 ~chan int | ~chan string, + C3 chan int | myChan, // single underlying type +]() { + type S0 []int + _ = make([]int, 10) + _ = make(S0, 10) + _ = make(S1, 10) + _ = make() /* ERROR not enough arguments */ + _ = make /* ERROR expects 2 or 3 arguments */ (S1) + _ = make(S1, 10, 20) + _ = make /* ERROR expects 2 or 3 arguments */ (S1, 10, 20, 30) + _ = make(S2 /* ERROR cannot make S2: no structural type */ , 10) + + type M0 map[string]int + _ = make(map[string]int) + _ = make(M0) + _ = make(M1) + _ = make(M1, 10) + _ = make/* ERROR expects 1 or 2 arguments */(M1, 10, 20) + _ = make(M2 /* ERROR cannot make M2: no structural type */ ) + + type C0 chan int + _ = make(chan int) + _ = make(C0) + _ = make(C1) + _ = make(C1, 10) + _ = make/* ERROR expects 1 or 2 arguments */(C1, 10, 20) + _ = make(C2 /* ERROR cannot make C2: no structural type */ ) + _ = make(C3) +} + +// unsafe.Alignof + +func _[T comparable]() { + var ( + b int64 + a [10]T + s struct{ f T } + p *T + l []T + f func(T) + i interface{ m() T } + c chan T + m map[T]T + t T + ) + + const bb = unsafe.Alignof(b) + assert(bb == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(a) + const _ = unsafe /* ERROR not constant */ .Alignof(s) + const pp = unsafe.Alignof(p) + assert(pp == 8) + const ll = unsafe.Alignof(l) + assert(ll == 8) + const ff = unsafe.Alignof(f) + assert(ff == 8) + const ii = unsafe.Alignof(i) + assert(ii == 8) + const cc = unsafe.Alignof(c) + assert(cc == 8) + const mm = unsafe.Alignof(m) + assert(mm == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(t) +} + +// unsafe.Offsetof + +func _[T comparable]() { + var ( + b struct{ _, f int64 } + a struct{ _, f [10]T } + s struct{ _, f struct{ f T } } + p struct{ _, f *T } + l struct{ _, f []T } + f struct{ _, f func(T) } + i struct{ _, f interface{ m() T } } + c struct{ _, f chan T } + m struct{ _, f map[T]T } + t struct{ _, f T } + ) + + const bb = unsafe.Offsetof(b.f) + assert(bb == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(a) + const _ = unsafe /* ERROR not constant */ .Alignof(s) + const pp = unsafe.Offsetof(p.f) + assert(pp == 8) + const ll = unsafe.Offsetof(l.f) + assert(ll == 24) + const ff = unsafe.Offsetof(f.f) + assert(ff == 8) + const ii = unsafe.Offsetof(i.f) + assert(ii == 16) + const cc = unsafe.Offsetof(c.f) + assert(cc == 8) + const mm = unsafe.Offsetof(m.f) + assert(mm == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(t) +} + +// unsafe.Sizeof + +func _[T comparable]() { + var ( + b int64 + a [10]T + s struct{ f T } + p *T + l []T + f func(T) + i interface{ m() T } + c chan T + m map[T]T + t T + ) + + const bb = unsafe.Sizeof(b) + assert(bb == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(a) + const _ = unsafe /* ERROR not constant */ .Alignof(s) + const pp = unsafe.Sizeof(p) + assert(pp == 8) + const ll = unsafe.Sizeof(l) + assert(ll == 24) + const ff = unsafe.Sizeof(f) + assert(ff == 8) + const ii = unsafe.Sizeof(i) + assert(ii == 16) + const cc = unsafe.Sizeof(c) + assert(cc == 8) + const mm = unsafe.Sizeof(m) + assert(mm == 8) + const _ = unsafe /* ERROR not constant */ .Alignof(t) } diff --git a/libgo/go/go/types/testdata/check/builtins.src b/libgo/go/go/types/testdata/check/builtins.src index 3707528..7fd6a4b 100644 --- a/libgo/go/go/types/testdata/check/builtins.src +++ b/libgo/go/go/types/testdata/check/builtins.src @@ -144,7 +144,7 @@ func close1() { var r <-chan int close() // ERROR not enough arguments close(1, 2) // ERROR too many arguments - close(42 /* ERROR not a channel */) + close(42 /* ERROR cannot close non-channel */) close(r /* ERROR receive-only channel */) close(c) _ = close /* ERROR used as value */ (c) diff --git a/libgo/go/go/types/testdata/check/const0.src b/libgo/go/go/types/testdata/check/const0.src index 5608b15..3cffdf9 100644 --- a/libgo/go/go/types/testdata/check/const0.src +++ b/libgo/go/go/types/testdata/check/const0.src @@ -27,7 +27,7 @@ const ( ub1 = true ub2 = 2 < 1 ub3 = ui1 == uf1 - ub4 = true /* ERROR "cannot convert" */ == 0 + ub4 = true /* ERROR "mismatched types untyped bool and untyped int" */ == 0 // integer values ui0 = 0 diff --git a/libgo/go/go/types/testdata/check/cycles.src b/libgo/go/go/types/testdata/check/cycles.src index 218b4ca..27b6111 100644 --- a/libgo/go/go/types/testdata/check/cycles.src +++ b/libgo/go/go/types/testdata/check/cycles.src @@ -45,6 +45,7 @@ type ( // pointers P0 *P0 + PP *struct{ PP.f /* ERROR no field or method f */ } // functions F0 func(F0) diff --git a/libgo/go/go/types/testdata/check/cycles4.src b/libgo/go/go/types/testdata/check/cycles4.src index 445babc..924aabf 100644 --- a/libgo/go/go/types/testdata/check/cycles4.src +++ b/libgo/go/go/types/testdata/check/cycles4.src @@ -4,6 +4,8 @@ package p +import "unsafe" + // Check that all methods of T are collected before // determining the result type of m (which embeds // all methods of T). @@ -13,7 +15,7 @@ type T interface { E } -var _ = T.m(nil).m().e() +var _ int = T.m(nil).m().e() type E interface { e() int @@ -22,7 +24,7 @@ type E interface { // Check that unresolved forward chains are followed // (see also comment in resolver.go, checker.typeDecl). -var _ = C.m(nil).m().e() +var _ int = C.m(nil).m().e() type A B @@ -108,3 +110,12 @@ type Element interface { type Event interface { Target() Element } + +// Check that accessing an interface method too early doesn't lead +// to follow-on errors due to an incorrectly computed type set. + +type T8 interface { + m() [unsafe.Sizeof(T8.m /* ERROR undefined */ )]int +} + +var _ = T8.m // no error expected here diff --git a/libgo/go/go/types/testdata/check/cycles5.src b/libgo/go/go/types/testdata/check/cycles5.src index 397adcc..c932ef92 100644 --- a/libgo/go/go/types/testdata/check/cycles5.src +++ b/libgo/go/go/types/testdata/check/cycles5.src @@ -135,7 +135,7 @@ type ( type ( a struct{ *b } b = c - c struct{ *b } + c struct{ *b /* ERROR invalid use of type alias */ } ) // issue #24939 @@ -145,7 +145,7 @@ type ( } M interface { - F() P + F() P // ERROR invalid use of type alias } P = interface { diff --git a/libgo/go/go/types/testdata/check/decls0.src b/libgo/go/go/types/testdata/check/decls0.src index 5ad8f53..18f0d32 100644 --- a/libgo/go/go/types/testdata/check/decls0.src +++ b/libgo/go/go/types/testdata/check/decls0.src @@ -4,7 +4,7 @@ // type declarations -package decls0 +package go1_17 // don't permit non-interface elements in interfaces import "unsafe" @@ -146,7 +146,7 @@ type ( m1(I5) } I6 interface { - S0 /* ERROR "not an interface" */ + S0 /* ERROR "non-interface type S0" */ } I7 interface { I1 @@ -187,10 +187,10 @@ func f4() (x *f4 /* ERROR "not a type" */ ) { return } // TODO(#43215) this should be detected as a cycle error func f5([unsafe.Sizeof(f5)]int) {} -func (S0) m1 (x S0 /* ERROR value .* is not a type */ .m1) {} -func (S0) m2 (x *S0 /* ERROR value .* is not a type */ .m2) {} -func (S0) m3 () (x S0 /* ERROR value .* is not a type */ .m3) { return } -func (S0) m4 () (x *S0 /* ERROR value .* is not a type */ .m4) { return } +func (S0) m1 (x S0 /* ERROR illegal cycle in method declaration */ .m1) {} +func (S0) m2 (x *S0 /* ERROR illegal cycle in method declaration */ .m2) {} +func (S0) m3 () (x S0 /* ERROR illegal cycle in method declaration */ .m3) { return } +func (S0) m4 () (x *S0 /* ERROR illegal cycle in method declaration */ .m4) { return } // interfaces may not have any blank methods type BlankI interface { diff --git a/libgo/go/go/types/testdata/check/decls1.src b/libgo/go/go/types/testdata/check/decls1.src index f4d2eab..6fe349b 100644 --- a/libgo/go/go/types/testdata/check/decls1.src +++ b/libgo/go/go/types/testdata/check/decls1.src @@ -83,7 +83,7 @@ var ( // Constant expression initializations var ( - v1 = 1 /* ERROR "cannot convert" */ + "foo" + v1 = 1 /* ERROR "mismatched types untyped int and untyped string" */ + "foo" v2 = c + 255 v3 = c + 256 /* ERROR "overflows" */ v4 = r + 2147483647 diff --git a/libgo/go/go/types/testdata/check/errors.src b/libgo/go/go/types/testdata/check/errors.src index ff92921..7cdc5fb 100644 --- a/libgo/go/go/types/testdata/check/errors.src +++ b/libgo/go/go/types/testdata/check/errors.src @@ -8,32 +8,38 @@ package errors // (matching messages are regular expressions, hence the \'s). func f(x int, m map[string]int) { // no values - _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m) + _ = f /* ERROR f\(0, m\) \(no value\) used as value */ (0, m) // built-ins - _ = println /* ERROR "println \(built-in\) must be called" */ + _ = println // ERROR println \(built-in\) must be called // types - _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */ + _ = complex128 // ERROR complex128 \(type\) is not an expression // constants const c1 = 991 const c2 float32 = 0.5 - 0 /* ERROR "0 \(untyped int constant\) is not used" */ - c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */ - c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */ - c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2 + const c3 = "foo" + 0 // ERROR 0 \(untyped int constant\) is not used + 0.5 // ERROR 0.5 \(untyped float constant\) is not used + "foo" // ERROR "foo" \(untyped string constant\) is not used + c1 // ERROR c1 \(untyped int constant 991\) is not used + c2 // ERROR c2 \(constant 0.5 of type float32\) is not used + c1 /* ERROR c1 \+ c2 \(constant 991.5 of type float32\) is not used */ + c2 + c3 // ERROR c3 \(untyped string constant "foo"\) is not used // variables - x /* ERROR "x \(variable of type int\) is not used" */ + x // ERROR x \(variable of type int\) is not used // values - x /* ERROR "x != x \(untyped bool value\) is not used" */ != x - x /* ERROR "x \+ x \(value of type int\) is not used" */ + x + nil // ERROR nil is not used + ( /* ERROR \(\*int\)\(nil\) \(value of type \*int\) is not used */ *int)(nil) + x /* ERROR x != x \(untyped bool value\) is not used */ != x + x /* ERROR x \+ x \(value of type int\) is not used */ + x // value, ok's const s = "foo" - m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s] + m /* ERROR m\[s\] \(map index expression of type int\) is not used */ [s] } // Valid ERROR comments can have a variety of forms. diff --git a/libgo/go/go/types/testdata/check/expr1.src b/libgo/go/go/types/testdata/check/expr1.src index 4ead815..42b95fb 100644 --- a/libgo/go/go/types/testdata/check/expr1.src +++ b/libgo/go/go/types/testdata/check/expr1.src @@ -111,10 +111,10 @@ type mystring string func _(x, y string, z mystring) { x = x + "foo" x = x /* ERROR not defined */ - "foo" - x = x + 1 // ERROR cannot convert + x = x /* ERROR mismatched types string and untyped int */ + 1 x = x + y x = x /* ERROR not defined */ - y - x = x * 10 // ERROR cannot convert + x = x /* ERROR mismatched types string and untyped int */* 10 } func f() (a, b int) { return } diff --git a/libgo/go/go/types/testdata/check/expr2.src b/libgo/go/go/types/testdata/check/expr2.src index 0c959e8..8757fd9 100644 --- a/libgo/go/go/types/testdata/check/expr2.src +++ b/libgo/go/go/types/testdata/check/expr2.src @@ -10,7 +10,7 @@ func _bool() { const t = true == true const f = true == false _ = t /* ERROR "cannot compare" */ < f - _ = 0 /* ERROR "cannot convert" */ == t + _ = 0 /* ERROR "mismatched types untyped int and untyped bool" */ == t var b bool var x, y float32 b = x < y @@ -29,7 +29,7 @@ func arrays() { _ = a == b _ = a != b _ = a /* ERROR < not defined */ < b - _ = a == nil /* ERROR cannot convert */ + _ = a /* ERROR cannot compare.*mismatched types */ == nil type C [10]int var c C @@ -53,7 +53,7 @@ func structs() { _ = s == t _ = s != t _ = s /* ERROR < not defined */ < t - _ = s == nil /* ERROR cannot convert */ + _ = s /* ERROR cannot compare.*mismatched types */ == nil type S struct { x int diff --git a/libgo/go/go/types/testdata/check/expr3.src b/libgo/go/go/types/testdata/check/expr3.src index 0525a5a..b8f96dc 100644 --- a/libgo/go/go/types/testdata/check/expr3.src +++ b/libgo/go/go/types/testdata/check/expr3.src @@ -44,9 +44,9 @@ func indexes() { _ = a[:10:10] _ = a[:11 /* ERROR "index .* out of bounds" */ :10] _ = a[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = a[10:0:10] /* ERROR swapped slice indices" */ - _ = a[0:10:0] /* ERROR "swapped slice indices" */ - _ = a[10:0:0] /* ERROR "swapped slice indices" */ + _ = a[10:0 /* ERROR "invalid slice indices" */ :10] + _ = a[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = a[10:0 /* ERROR "invalid slice indices" */:0] _ = &a /* ERROR "cannot take address" */ [:10] pa := &a @@ -62,9 +62,9 @@ func indexes() { _ = pa[:10:10] _ = pa[:11 /* ERROR "index .* out of bounds" */ :10] _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = pa[10:0:10] /* ERROR "swapped slice indices" */ - _ = pa[0:10:0] /* ERROR "swapped slice indices" */ - _ = pa[10:0:0] /* ERROR "swapped slice indices" */ + _ = pa[10:0 /* ERROR "invalid slice indices" */ :10] + _ = pa[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = pa[10:0 /* ERROR "invalid slice indices" */ :0] _ = &pa /* ERROR "cannot take address" */ [:10] var b [0]int @@ -82,16 +82,16 @@ func indexes() { _ = s[: - /* ERROR "negative" */ 1] _ = s[0] _ = s[1:2] - _ = s[2:1] /* ERROR "swapped slice indices" */ + _ = s[2:1 /* ERROR "invalid slice indices" */ ] _ = s[2:] _ = s[: 1 /* ERROR "overflows" */ <<100] _ = s[1 /* ERROR "overflows" */ <<100 :] _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] _ = s[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ] _ = s[:10:10] - _ = s[10:0:10] /* ERROR "swapped slice indices" */ - _ = s[0:10:0] /* ERROR "swapped slice indices" */ - _ = s[10:0:0] /* ERROR "swapped slice indices" */ + _ = s[10:0 /* ERROR "invalid slice indices" */ :10] + _ = s[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = s[10:0 /* ERROR "invalid slice indices" */ :0] _ = &s /* ERROR "cannot take address" */ [:10] var m map[string]int @@ -103,14 +103,14 @@ func indexes() { var ok mybool _, ok = m["bar"] _ = ok - _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "cannot convert" + _ = m/* ERROR "mismatched types int and untyped string" */[0 /* ERROR "cannot use 0" */ ] + "foo" var t string _ = t[- /* ERROR "negative" */ 1] _ = t[- /* ERROR "negative" */ 1 :] _ = t[: - /* ERROR "negative" */ 1] - _ = t /* ERROR "3-index slice of string" */ [1:2:3] - _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3] + _ = t[1:2:3 /* ERROR "3-index slice of string" */ ] + _ = "foo"[1:2:3 /* ERROR "3-index slice of string" */ ] var t0 byte t0 = t[0] _ = t0 @@ -458,7 +458,7 @@ func type_asserts() { var t I _ = t /* ERROR "use of .* outside type switch" */ .(type) - _ = t /* ERROR "missing method m" */ .(T) + _ = t /* ERROR "m has pointer receiver" */ .(T) _ = t.(*T) _ = t /* ERROR "missing method m" */ .(T1) _ = t /* ERROR "wrong type for method m" */ .(T2) @@ -493,20 +493,20 @@ func _calls() { f1(0) f1(x) f1(10.0) - f1() /* ERROR "not enough arguments" */ - f1(x, y /* ERROR "too many arguments" */ ) + f1() /* ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)" */ + f1(x, y /* ERROR "too many arguments in call to f1\n\thave \(int, float32\)\n\twant \(int\)" */ ) f1(s /* ERROR "cannot use .* in argument" */ ) f1(x ... /* ERROR "cannot use ..." */ ) f1(g0 /* ERROR "used as value" */ ()) f1(g1()) - f1(g2 /* ERROR "too many arguments" */ ()) + f1(g2 /* ERROR "too many arguments in call to f1\n\thave \(float32, string\)\n\twant \(int\)" */ ()) - f2() /* ERROR "not enough arguments" */ - f2(3.14) /* ERROR "not enough arguments" */ + f2() /* ERROR "not enough arguments in call to f2\n\thave \(\)\n\twant \(float32, string\)" */ + f2(3.14) /* ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(float32, string\)" */ f2(3.14, "foo") f2(x /* ERROR "cannot use .* in argument" */ , "foo") f2(g0 /* ERROR "used as value" */ ()) - f2(g1()) /* ERROR "not enough arguments" */ + f2(g1()) /* ERROR "not enough arguments in call to f2\n\thave \(int\)\n\twant \(float32, string\)" */ f2(g2()) fs() /* ERROR "not enough arguments" */ diff --git a/libgo/go/go/types/testdata/check/issues.go2 b/libgo/go/go/types/testdata/check/issues.go2 index 8994164..c164825 100644 --- a/libgo/go/go/types/testdata/check/issues.go2 +++ b/libgo/go/go/types/testdata/check/issues.go2 @@ -24,55 +24,46 @@ func _() { eql[io.Reader](nil, nil) } -// If we have a receiver of pointer type (below: *T) we must ignore -// the pointer in the implementation of the method lookup because -// the type bound of T is an interface and pointer to interface types -// have no methods and then the lookup would fail. +// If we have a receiver of pointer to type parameter type (below: *T) +// we don't have any methods, like for interfaces. type C[T any] interface { m() } // using type bound C func _[T C[T]](x *T) { - x.m() + x.m /* ERROR x\.m undefined */ () } // using an interface literal as bound func _[T interface{ m() }](x *T) { - x.m() + x.m /* ERROR x\.m undefined */ () } -// In a generic function body all method calls will be pointer method calls. -// If necessary, the function body will insert temporary variables, not seen -// by the user, in order to get an addressable variable to use to call the method. -// Thus, assume an argument type for a generic function to be the type of addressable -// values in the generic function when checking if the argument type satisfies the -// generic function's type bound. -func f2[_ interface{ m1(); m2() }]() +func f2[_ interface{ m1(); m2() }]() {} type T struct{} func (T) m1() func (*T) m2() func _() { - // TODO(rFindley) this error should be positioned on the 'T'. - f2 /* ERROR wrong method signature */ [T]() + f2[T /* ERROR m2 has pointer receiver */ ]() f2[*T]() } // When a type parameter is used as an argument to instantiate a parameterized -// type with a type list constraint, all of the type argument's types in its -// bound, but at least one (!), must be in the type list of the bound of the +// type with a type set constraint, all of the type argument's types in its +// bound, but at least one (!), must be in the type set of the bound of the // corresponding parameterized type's type parameter. -type T1[P interface{type uint}] struct{} +type T1[P interface{~uint}] struct{} func _[P any]() { - _ = T1[P /* ERROR P has no type constraints */ ]{} + _ = T1[P /* ERROR P does not implement interface{~uint} */ ]{} } // This is the original (simplified) program causing the same issue. type Unsigned interface { - type uint + ~uint } type T2[U Unsigned] struct { @@ -83,8 +74,8 @@ func (u T2[U]) Add1() U { return u.s + 1 } -func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] { - return T2[U /* ERROR U has no type constraints */ ]{} +func NewT2[U any]() T2[U /* ERROR U does not implement Unsigned */ ] { + return T2[U /* ERROR U does not implement Unsigned */ ]{} } func _() { @@ -160,10 +151,10 @@ type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] } // The implementation of conversions T(x) between integers and floating-point // numbers checks that both T and x have either integer or floating-point // type. When the type of T or x is a type parameter, the respective simple -// predicate disjunction in the implementation was wrong because if a type list +// predicate disjunction in the implementation was wrong because if a term list // contains both an integer and a floating-point type, the type parameter is // neither an integer or a floating-point number. -func convert[T1, T2 interface{type int, uint, float32}](v T1) T2 { +func convert[T1, T2 interface{~int | ~uint | ~float32}](v T1) T2 { return T2(v) } @@ -175,12 +166,12 @@ func _() { // both numeric, or both strings. The implementation had the same problem // with this check as the conversion issue above (issue #39623). -func issue39623[T interface{type int, string}](x, y T) T { +func issue39623[T interface{~int | ~string}](x, y T) T { return x + y } // Simplified, from https://go2goplay.golang.org/p/efS6x6s-9NI: -func Sum[T interface{type int, string}](s []T) (sum T) { +func Sum[T interface{~int | ~string}](s []T) (sum T) { for _, v := range s { sum += v } @@ -189,19 +180,19 @@ func Sum[T interface{type int, string}](s []T) (sum T) { // Assignability of an unnamed pointer type to a type parameter that // has a matching underlying type. -func _[T interface{}, PT interface{type *T}] (x T) PT { +func _[T interface{}, PT interface{~*T}] (x T) PT { return &x } -// Indexing of generic types containing type parameters in their type list: -func at[T interface{ type []E }, E interface{}](x T, i int) E { +// Indexing of generic types containing type parameters in their term list: +func at[T interface{ ~[]E }, E interface{}](x T, i int) E { return x[i] } // A generic type inside a function acts like a named type. Its underlying -// type is itself, its "operational type" is defined by the type list in +// type is itself, its "operational type" is defined by the term list in // the tybe bound, if any. -func _[T interface{type int}](x T) { +func _[T interface{~int}](x T) { type myint int var _ int = int(x) var _ T = 42 @@ -210,38 +201,45 @@ func _[T interface{type int}](x T) { // Indexing a generic type with an array type bound checks length. // (Example by mdempsky@.) -func _[T interface { type [10]int }](x T) { +func _[T interface { ~[10]int }](x T) { _ = x[9] // ok _ = x[20 /* ERROR out of bounds */ ] } // Pointer indirection of a generic type. -func _[T interface{ type *int }](p T) int { +func _[T interface{ ~*int }](p T) int { return *p } // Channel sends and receives on generic types. -func _[T interface{ type chan int }](ch T) int { +func _[T interface{ ~chan int }](ch T) int { ch <- 0 return <- ch } // Calling of a generic variable. -func _[T interface{ type func() }](f T) { +func _[T interface{ ~func() }](f T) { f() go f() } -// We must compare against the underlying type of type list entries +type F1 func() +type F2 func() +func _[T interface{ func()|F1|F2 }](f T) { + f() + go f() +} + +// We must compare against the underlying type of term list entries // when checking if a constraint is satisfied by a type. The under- -// lying type of each type list entry must be computed after the +// lying type of each term list entry must be computed after the // interface has been instantiated as its typelist may contain a // type parameter that was substituted with a defined type. // Test case from an (originally) failing example. -type sliceOf[E any] interface{ type []E } +type sliceOf[E any] interface{ ~[]E } -func append[T interface{}, S sliceOf[T], T2 interface{ type T }](s S, t ...T2) S +func append[T interface{}, S sliceOf[T], T2 interface{}](s S, t ...T2) S { panic(0) } var f func() var cancelSlice []context.CancelFunc @@ -249,7 +247,7 @@ var _ = append[context.CancelFunc, []context.CancelFunc, context.CancelFunc](can // A generic function must be instantiated with a type, not a value. -func g[T any](T) T +func g[T any](T) T { panic(0) } var _ = g[int] var _ = g[nil /* ERROR is not a type */ ] diff --git a/libgo/go/go/types/testdata/check/issues.src b/libgo/go/go/types/testdata/check/issues.src index 55fe220..0b77b0e 100644 --- a/libgo/go/go/types/testdata/check/issues.src +++ b/libgo/go/go/types/testdata/check/issues.src @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package issues +package go1_17 // don't permit non-interface elements in interfaces import ( "fmt" @@ -79,11 +79,11 @@ func issue9473(a []int, b ...int) { // Check that embedding a non-interface type in an interface results in a good error message. func issue10979() { type _ interface { - int /* ERROR int is not an interface */ + int /* ERROR non-interface type int */ } type T struct{} type _ interface { - T /* ERROR T is not an interface */ + T /* ERROR non-interface type T */ } type _ interface { nosuchtype /* ERROR undeclared name: nosuchtype */ @@ -165,8 +165,8 @@ func issue10260() { _ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ } _ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ } - make(chan I1) <- i0 /* ERROR cannot use .* in send: missing method foo */ - make(chan I1) <- i2 /* ERROR cannot use .* in send: 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\(\)\) */ } // Check that constants representable as integers are in integer form @@ -280,7 +280,7 @@ type issue25301b /* ERROR cycle */ = interface { } type issue25301c interface { - notE // ERROR struct\{\} is not an interface + notE // ERROR non-interface type struct\{\} } type notE = struct{} diff --git a/libgo/go/go/types/testdata/check/linalg.go2 b/libgo/go/go/types/testdata/check/linalg.go2 index 0d27603..f02e773 100644 --- a/libgo/go/go/types/testdata/check/linalg.go2 +++ b/libgo/go/go/types/testdata/check/linalg.go2 @@ -4,15 +4,13 @@ package linalg -import "math" - // Numeric is type bound that matches any numeric type. // It would likely be in a constraints package in the standard library. type Numeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - complex64, complex128 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~complex64 | ~complex128 } func DotProduct[T Numeric](s1, s2 []T) T { @@ -42,42 +40,43 @@ func AbsDifference[T NumericAbs[T]](a, b T) T { // OrderedNumeric is a type bound that matches numeric types that support the < operator. type OrderedNumeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 } // Complex is a type bound that matches the two complex types, which do not have a < operator. type Complex interface { - type complex64, complex128 -} - -// OrderedAbs is a helper type that defines an Abs method for -// ordered numeric types. -type OrderedAbs[T OrderedNumeric] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] { - if a < 0 { - return -a - } - return a + ~complex64 | ~complex128 } -// ComplexAbs is a helper type that defines an Abs method for -// complex types. -type ComplexAbs[T Complex] T - -func (a ComplexAbs[T]) Abs() ComplexAbs[T] { - r := float64(real(a)) - i := float64(imag(a)) - d := math.Sqrt(r * r + i * i) - return ComplexAbs[T](complex(d, 0)) -} - -func OrderedAbsDifference[T OrderedNumeric](a, b T) T { - return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b))) -} - -func ComplexAbsDifference[T Complex](a, b T) T { - return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b))) -} +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // OrderedAbs is a helper type that defines an Abs method for +// // ordered numeric types. +// type OrderedAbs[T OrderedNumeric] T +// +// func (a OrderedAbs[T]) Abs() OrderedAbs[T] { +// if a < 0 { +// return -a +// } +// return a +// } +// +// // ComplexAbs is a helper type that defines an Abs method for +// // complex types. +// type ComplexAbs[T Complex] T +// +// func (a ComplexAbs[T]) Abs() ComplexAbs[T] { +// r := float64(real(a)) +// i := float64(imag(a)) +// d := math.Sqrt(r * r + i * i) +// return ComplexAbs[T](complex(d, 0)) +// } +// +// func OrderedAbsDifference[T OrderedNumeric](a, b T) T { +// return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b))) +// } +// +// func ComplexAbsDifference[T Complex](a, b T) T { +// return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b))) +// } diff --git a/libgo/go/go/types/testdata/check/main.go2 b/libgo/go/go/types/testdata/check/main.go2 index 65e9aa2..fb567a0 100644 --- a/libgo/go/go/types/testdata/check/main.go2 +++ b/libgo/go/go/types/testdata/check/main.go2 @@ -4,4 +4,4 @@ package main -func main[ /* ERROR "func main must have no type parameters" */ T any]() {} +func main[T /* ERROR "func main must have no type parameters" */ any]() {} diff --git a/libgo/go/go/types/testdata/check/map2.go2 b/libgo/go/go/types/testdata/check/map2.go2 index 2833445..e13bf33 100644 --- a/libgo/go/go/types/testdata/check/map2.go2 +++ b/libgo/go/go/types/testdata/check/map2.go2 @@ -114,7 +114,7 @@ func (it *Iterator[K, V]) Next() (K, V, bool) { // chans -func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) +func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) { panic(0) } // A sender is used to send values to a Receiver. type chans_Sender[T any] struct { @@ -143,4 +143,4 @@ type chans_Receiver[T any] struct { func (r *chans_Receiver[T]) Next() (T, bool) { v, ok := <-r.values return v, ok -}
\ No newline at end of file +} diff --git a/libgo/go/go/types/testdata/check/methodsets.src b/libgo/go/go/types/testdata/check/methodsets.src index 9fb10de..b0eb14cf 100644 --- a/libgo/go/go/types/testdata/check/methodsets.src +++ b/libgo/go/go/types/testdata/check/methodsets.src @@ -196,9 +196,9 @@ func issue5918() { _ func(error) string = error.Error perr = &err - _ = perr.Error /* ERROR "no field or method" */ () - _ func() string = perr.Error /* ERROR "no field or method" */ - _ func(*error) string = (*error).Error /* ERROR "no field or method" */ + _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ () + _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ + _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */ ) type T *interface{ m() int } @@ -207,8 +207,8 @@ func issue5918() { _ = (*x).m() _ = (*x).m - _ = x.m /* ERROR "no field or method" */ () - _ = x.m /* ERROR "no field or method" */ - _ = T.m /* ERROR "no field or method" */ + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ () + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ + _ = T.m /* ERROR "type T is pointer to interface, not interface" */ ) } diff --git a/libgo/go/go/types/testdata/check/shifts.src b/libgo/go/go/types/testdata/check/shifts.src index 4d3c59a..16a67ae 100644 --- a/libgo/go/go/types/testdata/check/shifts.src +++ b/libgo/go/go/types/testdata/check/shifts.src @@ -380,7 +380,7 @@ func issue21727() { var a = make([]int, 1<<s + 1.2 /* ERROR "truncated to int" */ ) var _ = a[1<<s - 2.3 /* ERROR "truncated to int" */ ] var _ int = 1<<s + 3.4 /* ERROR "truncated to int" */ - var _ = string(1 << s) + var _ = string(1 /* ERROR shifted operand 1 .* must be integer */ << s) var _ = string(1.0 /* ERROR "cannot convert" */ << s) } diff --git a/libgo/go/go/types/testdata/check/stmt0.src b/libgo/go/go/types/testdata/check/stmt0.src index 76b6e70..ec8bf71 100644 --- a/libgo/go/go/types/testdata/check/stmt0.src +++ b/libgo/go/go/types/testdata/check/stmt0.src @@ -29,10 +29,10 @@ func assignments0() (int, int) { a, b, c = <- /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ ch - return /* ERROR "wrong number of return values" */ - return /* ERROR "wrong number of return values" */ 1 + return /* ERROR "not enough return values\n\thave \(\)\n\twant \(int, int\)" */ + return 1 /* ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)" */ return 1, 2 - return /* ERROR "wrong number of return values" */ 1, 2, 3 + return 1, 2, 3 /* ERROR "too many return values\n\thave \(number, number, number\)\n\twant \(int, int\)" */ } func assignments1() { @@ -49,18 +49,18 @@ func assignments1() { b = true i += 1 - i += "foo" /* ERROR "cannot convert.*int" */ + i /* ERROR "mismatched types int and untyped string" */+= "foo" f -= 1 f /= 0 f = float32(0)/0 /* ERROR "division by zero" */ - f -= "foo" /* ERROR "cannot convert.*float64" */ + f /* ERROR "mismatched types float64 and untyped string" */-= "foo" c *= 1 c /= 0 s += "bar" - s += 1 /* ERROR "cannot convert.*string" */ + s /* ERROR "mismatched types string and untyped int" */+= 1 var u64 uint64 u64 += 1<<u64 @@ -69,10 +69,10 @@ func assignments1() { // test cases for issue 5800 var ( - _ int = nil /* ERROR "untyped nil value" */ - _ [10]int = nil /* ERROR "untyped nil value" */ + _ int = nil /* ERROR "cannot use nil as int value in variable declaration" */ + _ [10]int = nil /* ERROR "cannot use nil as \[10\]int value in variable declaration" */ _ []byte = nil - _ struct{} = nil /* ERROR "untyped nil value" */ + _ struct{} = nil /* ERROR "cannot use nil as struct{} value in variable declaration" */ _ func() = nil _ map[int]string = nil _ chan int = nil @@ -81,7 +81,7 @@ func assignments1() { // test cases for issue 5500 _ = func() (int, bool) { var m map[int]int - return /* ERROR "wrong number of return values" */ m[0] + return m /* ERROR "not enough return values" */ [0] } g := func(int, bool){} @@ -375,20 +375,20 @@ func continues() { func returns0() { return - return 0 /* ERROR no result values expected */ + return 0 /* ERROR too many return values */ } func returns1(x float64) (int, *float64) { return 0, &x - return /* ERROR wrong number of return values */ + return /* ERROR not enough return values */ return "foo" /* ERROR "cannot .* in return statement" */, x /* ERROR "cannot use .* in return statement" */ - return /* ERROR wrong number of return values */ 0, &x, 1 + return 0, &x, 1 /* ERROR too many return values */ } func returns2() (a, b int) { return return 1, "foo" /* ERROR cannot use .* in return statement */ - return /* ERROR wrong number of return values */ 1, 2, 3 + return 1, 2, 3 /* ERROR too many return values */ { type a int return 1, 2 @@ -695,7 +695,7 @@ func typeswitches() { _ = y } - switch x := i /* ERROR "not an interface" */ .(type) {} + switch x /* ERROR "x declared but not used" */ := i /* ERROR "not an interface" */ .(type) {} switch t := x.(type) { case nil: @@ -719,6 +719,18 @@ func typeswitches() { case T2 /* ERROR "wrong type for method m" */ : case I2 /* STRICT "wrong type for method m" */ : // only an error in strict mode (issue 8561) } + + + { + x := 1 + v := 2 + switch v /* ERROR "v [(]variable of type int[)] is not an interface" */ .(type) { + case int: + println(x) + println(x / 0 /* ERROR "invalid operation: division by zero" */) + case 1 /* ERROR "expected type, found 1" */: + } + } } // Test that each case clause uses the correct type of the variable @@ -937,13 +949,13 @@ func issue6766b() { // errors reported). func issue10148() { for y /* ERROR declared but not used */ := range "" { - _ = "" /* ERROR cannot convert */ + 1 + _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1 } for range 1 /* ERROR cannot range over 1 */ { - _ = "" /* ERROR cannot convert */ + 1 + _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1 } for y := range 1 /* ERROR cannot range over 1 */ { - _ = "" /* ERROR cannot convert */ + 1 + _ = "" /* ERROR mismatched types untyped string and untyped int */ + 1 } } diff --git a/libgo/go/go/types/testdata/check/tinference.go2 b/libgo/go/go/types/testdata/check/tinference.go2 deleted file mode 100644 index 31338b33..0000000 --- a/libgo/go/go/types/testdata/check/tinference.go2 +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2020 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 tinferenceB - -import "strconv" - -type any interface{} - -// TODO(rFindley) the below partially applied function types should probably -// not be permitted (spec question). - -func f0[A any, B interface{type C}, C interface{type D}, D interface{type A}](A, B, C, D) -func _() { - f := f0[string] - f("a", "b", "c", "d") - f0("a", "b", "c", "d") -} - -func f1[A any, B interface{type A}](A, B) -func _() { - f := f1[int] - f(int(0), int(0)) - f1(int(0), int(0)) -} - -func f2[A any, B interface{type []A}](A, B) -func _() { - f := f2[byte] - f(byte(0), []byte{}) - f2(byte(0), []byte{}) -} - -func f3[A any, B interface{type C}, C interface{type *A}](A, B, C) -func _() { - f := f3[int] - var x int - f(x, &x, &x) - f3(x, &x, &x) -} - -func f4[A any, B interface{type []C}, C interface{type *A}](A, B, C) -func _() { - f := f4[int] - var x int - f(x, []*int{}, &x) - f4(x, []*int{}, &x) -} - -func f5[A interface{type struct{b B; c C}}, B any, C interface{type *B}](x B) A -func _() { - x := f5(1.2) - var _ float64 = x.b - var _ float64 = *x.c -} - -func f6[A any, B interface{type struct{f []A}}](B) A -func _() { - x := f6(struct{f []string}{}) - var _ string = x -} - -// TODO(gri) Need to flag invalid recursive constraints. At the -// moment these cause infinite recursions and stack overflow. -// func f7[A interface{type B}, B interface{type A}]() - -// More realistic examples - -func Double[S interface{ type []E }, E interface{ type int, int8, int16, int32, int64 }](s S) S { - r := make(S, len(s)) - for i, v := range s { - r[i] = v + v - } - return r -} - -type MySlice []int - -var _ = Double(MySlice{1}) - -// From the draft design. - -type Setter[B any] interface { - Set(string) - type *B -} - -func FromStrings[T interface{}, PT Setter[T]](s []string) []T { - result := make([]T, len(s)) - for i, v := range s { - // The type of &result[i] is *T which is in the type list - // of Setter2, so we can convert it to PT. - p := PT(&result[i]) - // PT has a Set method. - p.Set(v) - } - return result -} - -type Settable int - -func (p *Settable) Set(s string) { - i, _ := strconv.Atoi(s) // real code should not ignore the error - *p = Settable(i) -} - -var _ = FromStrings[Settable]([]string{"1", "2"}) diff --git a/libgo/go/go/types/testdata/check/tmp.go2 b/libgo/go/go/types/testdata/check/tmp.go2 deleted file mode 100644 index dae78ca..0000000 --- a/libgo/go/go/types/testdata/check/tmp.go2 +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 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. - -// This file is meant as "dumping ground" for debugging code. - -package p - -// fun test case -type C[P interface{m()}] P - -func (r C[P]) m() { r.m() } - -func f[T interface{m(); n()}](x T) { - y := C[T](x) - y.m() -} diff --git a/libgo/go/go/types/testdata/check/typeinst.go2 b/libgo/go/go/types/testdata/check/typeinst.go2 index 3184a4b..6423cb8 100644 --- a/libgo/go/go/types/testdata/check/typeinst.go2 +++ b/libgo/go/go/types/testdata/check/typeinst.go2 @@ -8,7 +8,8 @@ type myInt int // Parameterized type declarations -type T1[P any] P +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +type T1[P any] P // ERROR cannot use a type parameter as RHS in type declaration type T2[P any] struct { f P @@ -17,13 +18,15 @@ type T2[P any] struct { type List[P any] []P -// Alias type declarations cannot have type parameters. Syntax error. -type A1[P any] = /* ERROR cannot be alias */ P +// Alias type declarations cannot have type parameters. +// Issue #46477 proposses to change that. +type A1[P any] = /* ERROR cannot be alias */ struct{} -// But an alias may refer to a generic, uninstantiated type. -type A2 = List +// Pending clarification of #46477 we disallow aliases +// of generic types. +type A2 = List // ERROR cannot use generic type var _ A2[int] -var _ A2 /* ERROR without instantiation */ +var _ A2 type A3 = List[int] var _ A3 @@ -33,11 +36,11 @@ var _ A3 var x int type _ x /* ERROR not a type */ [int] -type _ int /* ERROR not a generic type */ [] -type _ myInt /* ERROR not a generic type */ [] +type _ int /* ERROR not a generic type */ [] // ERROR expected type argument list +type _ myInt /* ERROR not a generic type */ [] // ERROR expected type argument list // TODO(gri) better error messages -type _ T1 /* ERROR got 0 arguments but 1 type parameters */ [] +type _ T1[] // ERROR expected type argument list type _ T1[x /* ERROR not a type */ ] type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32] diff --git a/libgo/go/go/types/testdata/check/typeinst2.go2 b/libgo/go/go/types/testdata/check/typeinst2.go2 index 6e2104a..1c3eb21 100644 --- a/libgo/go/go/types/testdata/check/typeinst2.go2 +++ b/libgo/go/go/types/testdata/check/typeinst2.go2 @@ -85,27 +85,29 @@ type NumericAbs[T any] interface { Abs() T } -func AbsDifference[T NumericAbs[T]](x T) +func AbsDifference[T NumericAbs[T]](x T) { panic(0) } -type OrderedAbs[T any] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] - -func OrderedAbsDifference[T any](x T) { - AbsDifference(OrderedAbs[T](x)) -} +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type OrderedAbs[T any] T +// +// func (a OrderedAbs[T]) Abs() OrderedAbs[T] +// +// func OrderedAbsDifference[T any](x T) { +// AbsDifference(OrderedAbs[T](x)) +// } // same code, reduced to essence -func g[P interface{ m() P }](x P) +func g[P interface{ m() P }](x P) { panic(0) } -type T4[P any] P - -func (_ T4[P]) m() T4[P] - -func _[Q any](x Q) { - g(T4[Q](x)) -} +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type T4[P any] P +// +// func (_ T4[P]) m() T4[P] +// +// func _[Q any](x Q) { +// g(T4[Q](x)) +// } // Another test case that caused problems in the past @@ -148,78 +150,76 @@ func _[T any](r R2[T, int], p *R2[string, T]) { p.pm() } -// An interface can (explicitly) declare at most one type list. +// It is ok to have multiple embedded unions. type _ interface { m0() - type int, string, bool - type /* ERROR multiple type lists */ float32, float64 + ~int | ~string | ~bool + ~float32 | ~float64 m1() m2() - type /* ERROR multiple type lists */ complex64, complex128 - type /* ERROR multiple type lists */ rune + ~complex64 | ~complex128 + ~rune } -// Interface type lists may contain each type at most once. -// (If there are multiple lists, we assume the author intended -// for them to be all in a single list, and we report the error -// as well.) +// Type sets may contain each type at most once. type _ interface { - type int, int /* ERROR duplicate type int */ - type /* ERROR multiple type lists */ int /* ERROR duplicate type int */ + ~int|~ /* ERROR overlapping terms ~int */ int + ~int|int /* ERROR overlapping terms int */ + int|int /* ERROR overlapping terms int */ } type _ interface { - type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int} + ~struct{f int} | ~struct{g int} | ~ /* ERROR overlapping terms */ struct {f int} } -// Interface type lists can contain any type, incl. *Named types. +// Interface term lists can contain any type, incl. *Named types. // Verify that we use the underlying type to compute the operational type. type MyInt int -func add1[T interface{type MyInt}](x T) T { +func add1[T interface{MyInt}](x T) T { return x + 1 } type MyString string -func double[T interface{type MyInt, MyString}](x T) T { +func double[T interface{MyInt|MyString}](x T) T { return x + x } -// Embedding of interfaces with type lists leads to interfaces -// with type lists that are the intersection of the embedded -// type lists. +// Embedding of interfaces with term lists leads to interfaces +// with term lists that are the intersection of the embedded +// term lists. type E0 interface { - type int, bool, string + ~int | ~bool | ~string } type E1 interface { - type int, float64, string + ~int | ~float64 | ~string } type E2 interface { - type float64 + ~float64 } type I0 interface { E0 } -func f0[T I0]() +func f0[T I0]() {} var _ = f0[int] var _ = f0[bool] var _ = f0[string] -var _ = f0[float64 /* ERROR does not satisfy I0 */ ] +var _ = f0[float64 /* ERROR does not implement I0 */ ] type I01 interface { E0 E1 } -func f01[T I01]() +func f01[T I01]() {} var _ = f01[int] -var _ = f01[bool /* ERROR does not satisfy I0 */ ] +var _ = f01[bool /* ERROR does not implement I0 */ ] var _ = f01[string] -var _ = f01[float64 /* ERROR does not satisfy I0 */ ] +var _ = f01[float64 /* ERROR does not implement I0 */ ] type I012 interface { E0 @@ -227,30 +227,54 @@ type I012 interface { E2 } -func f012[T I012]() -var _ = f012[int /* ERROR does not satisfy I012 */ ] -var _ = f012[bool /* ERROR does not satisfy I012 */ ] -var _ = f012[string /* ERROR does not satisfy I012 */ ] -var _ = f012[float64 /* ERROR does not satisfy I012 */ ] +func f012[T I012]() {} +var _ = f012[int /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[bool /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[string /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[float64 /* ERROR cannot implement I012.*empty type set */ ] type I12 interface { E1 E2 } -func f12[T I12]() -var _ = f12[int /* ERROR does not satisfy I12 */ ] -var _ = f12[bool /* ERROR does not satisfy I12 */ ] -var _ = f12[string /* ERROR does not satisfy I12 */ ] +func f12[T I12]() {} +var _ = f12[int /* ERROR does not implement I12 */ ] +var _ = f12[bool /* ERROR does not implement I12 */ ] +var _ = f12[string /* ERROR does not implement I12 */ ] var _ = f12[float64] type I0_ interface { E0 - type int + ~int } -func f0_[T I0_]() +func f0_[T I0_]() {} var _ = f0_[int] -var _ = f0_[bool /* ERROR does not satisfy I0_ */ ] -var _ = f0_[string /* ERROR does not satisfy I0_ */ ] -var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ] +var _ = f0_[bool /* ERROR does not implement I0_ */ ] +var _ = f0_[string /* ERROR does not implement I0_ */ ] +var _ = f0_[float64 /* ERROR does not implement I0_ */ ] + +// Using a function instance as a type is an error. +var _ f0 // ERROR not a type +var _ f0 /* ERROR not a type */ [int] + +// Empty type sets can only be satisfied by empty type sets. +type none interface { + // force an empty type set + int + string +} + +func ff[T none]() {} +func gg[T any]() {} +func hh[T ~int]() {} + +func _[T none]() { + _ = ff[int /* ERROR cannot implement none \(empty type set\) */ ] + _ = ff[T] // pathological but ok because T's type set is empty, too + _ = gg[int] + _ = gg[T] + _ = hh[int] + _ = hh[T] +} diff --git a/libgo/go/go/types/testdata/check/typeparams.go2 b/libgo/go/go/types/testdata/check/typeparams.go2 index d95e02e..6d63d59 100644 --- a/libgo/go/go/types/testdata/check/typeparams.go2 +++ b/libgo/go/go/types/testdata/check/typeparams.go2 @@ -6,18 +6,16 @@ package p // import "io" // for type assertion tests -// The predeclared identifier "any" is only visible as a constraint -// in a type parameter list. -var _ any // ERROR undeclared -func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) { - var _ any /* ERROR undeclared */ +var _ any // ok to use any anywhere +func _[_ any, _ interface{any}](any) { + var _ any } func identity[T any](x T) T { return x } -func _[_ any](x int) int -func _[T any](T /* ERROR redeclared */ T)() -func _[T, T /* ERROR redeclared */ any]() +func _[_ any](x int) int { panic(0) } +func _[T any](T /* ERROR redeclared */ T)() {} +func _[T, T /* ERROR redeclared */ any]() {} // Constraints (incl. any) may be parenthesized. func _[_ (any)]() {} @@ -52,22 +50,22 @@ func swapswap[A, B any](a A, b B) (A, B) { type F[A, B any] func(A, B) (B, A) -func min[T interface{ type int }](x, y T) T { +func min[T interface{ ~int }](x, y T) T { if x < y { return x } return y } -func _[T interface{type int, float32}](x, y T) bool { return x < y } +func _[T interface{~int | ~float32}](x, y T) bool { return x < y } func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y } +func _[T interface{~int | ~float32 | ~bool}](x, y T) bool { return x /* ERROR cannot compare */ < y } func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y } func _[T C2[T]](x, y T) bool { return x < y } type C1[T any] interface{} -type C2[T any] interface{ type int, float32 } +type C2[T any] interface{ ~int | ~float32 } func new[T any]() *T { var x T @@ -77,66 +75,92 @@ func new[T any]() *T { var _ = new /* ERROR cannot use generic function new */ var _ *int = new[int]() -func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable +func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) {} // w/o constraint we don't know if T is comparable -func f1[T1 any](struct{T1}) int +func f1[T1 any](struct{T1 /* ERROR cannot be a .* type parameter */ }) int { panic(0) } var _ = f1[int](struct{T1}{}) type T1 = int -func f2[t1 any](struct{t1; x float32}) int +func f2[t1 any](struct{t1 /* ERROR cannot be a .* type parameter */ ; x float32}) int { panic(0) } var _ = f2[t1](struct{t1; x float32}{}) type t1 = int -func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int +func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int { panic(0) } var _ = f3[int, rune, bool](1, struct{x rune}{}, nil) // indexing func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int }] (x T, i int) { _ = x[i] } -func _[T interface{ type [10]int, *[20]int, map[int]int }] (x T, i int) { _ = x[i] } -func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } - -// indexing with various combinations of map types in type lists (see issue #42616) -func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = x[i] } -func _[T interface{ type []E }, E any](x T, i int) { _ = &x[i] } -func _[T interface{ type map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted -func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] } -func _[T interface{ type []E, map[int]E, map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types -func _[T interface{ type []E, map[string]E }, E any](x T, i int) { _ = x[i /* ERROR cannot use i */ ] } +func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } +func _[T interface{ ~string }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types +func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } +func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } + +// indexing with various combinations of map types in type sets (see issue #42616) +func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types +func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] } +func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted +func _[T interface{ ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] } +func _[T interface{ ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types +func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types + +// indexing with various combinations of array and other types in type sets +func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } +func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } +func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } + +// indexing with strings and non-variable arrays (assignment not permitted) +func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 } +func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 } +func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 } +func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 } +func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 } +func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 } // slicing -// TODO(gri) implement this -func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] } +func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] } +func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j:k] } +func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j:k] } +func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] } + +type myByte1 []byte +type myByte2 []byte +func _[T interface{ []byte | myByte1 | myByte2 }] (x T, i, j, k int) { var _ T = x[i:j:k] } +func _[T interface{ []byte | myByte1 | []int }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j:k] } + +func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] } +func _[T interface{ []byte | myByte1 | []int | string }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j] } // len/cap built-ins func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = len(x) } -func _[T interface{ type [10]int }](x T) { _ = len(x) } -func _[T interface{ type []byte }](x T) { _ = len(x) } -func _[T interface{ type map[int]int }](x T) { _ = len(x) } -func _[T interface{ type chan int }](x T) { _ = len(x) } -func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) } +func _[T interface{ ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string }](x T) { _ = len(x) } +func _[T interface{ ~[10]int }](x T) { _ = len(x) } +func _[T interface{ ~[]byte }](x T) { _ = len(x) } +func _[T interface{ ~map[int]int }](x T) { _ = len(x) } +func _[T interface{ ~chan int }](x T) { _ = len(x) } +func _[T interface{ ~string | ~[]byte | ~chan int }](x T) { _ = len(x) } func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type [10]int }](x T) { _ = cap(x) } -func _[T interface{ type []byte }](x T) { _ = cap(x) } -func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type chan int }](x T) { _ = cap(x) } -func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) } +func _[T interface{ ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~[10]int }](x T) { _ = cap(x) } +func _[T interface{ ~[]byte }](x T) { _ = cap(x) } +func _[T interface{ ~map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~chan int }](x T) { _ = cap(x) } +func _[T interface{ ~[]byte | ~chan int }](x T) { _ = cap(x) } // range iteration @@ -144,64 +168,136 @@ func _[T interface{}](x T) { for range x /* ERROR cannot range */ {} } -func _[T interface{ type string, []string }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } - for i, e := range x /* ERROR must have the same element type */ { _ = i } - for _, e := range x /* ERROR must have the same element type */ {} - var e rune - _ = e - for _, (e) = range x /* ERROR must have the same element type */ {} -} +type myString string +func _[ + B1 interface{ string }, + B2 interface{ string | myString }, -func _[T interface{ type string, []rune, map[int]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x { _ = i; _ = e } -} + C1 interface{ chan int }, + C2 interface{ chan int | <-chan int }, + C3 interface{ chan<- int }, -func _[T interface{ type string, []rune, map[string]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x /* ERROR must have the same key type */ { _ = e } -} + S1 interface{ []int }, + S2 interface{ []int | [10]int }, -func _[T interface{ type string, chan int }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value -} + A1 interface{ [10]int }, + A2 interface{ [10]int | []int }, + + P1 interface{ *[10]int }, + P2 interface{ *[10]int | *[]int }, + + M1 interface{ map[string]int }, + M2 interface{ map[string]int | map[string]string }, +]() { + var b0 string + for range b0 {} + for _ = range b0 {} + for _, _ = range b0 {} + + var b1 B1 + for range b1 {} + for _ = range b1 {} + for _, _ = range b1 {} + + var b2 B2 + for range b2 {} + + var c0 chan int + for range c0 {} + for _ = range c0 {} + for _, _ /* ERROR permits only one iteration variable */ = range c0 {} + + var c1 C1 + for range c1 {} + for _ = range c1 {} + for _, _ /* ERROR permits only one iteration variable */ = range c1 {} + + var c2 C2 + for range c2 {} + + var c3 C3 + for range c3 /* ERROR receive from send-only channel */ {} + + var s0 []int + for range s0 {} + for _ = range s0 {} + for _, _ = range s0 {} + + var s1 S1 + for range s1 {} + for _ = range s1 {} + for _, _ = range s1 {} + + var s2 S2 + for range s2 /* ERROR cannot range over s2.*no structural type */ {} -func _[T interface{ type string, chan<-int }](x T) { - for i := range x /* ERROR send-only channel */ { _ = i } + var a0 []int + for range a0 {} + for _ = range a0 {} + for _, _ = range a0 {} + + var a1 A1 + for range a1 {} + for _ = range a1 {} + for _, _ = range a1 {} + + var a2 A2 + for range a2 /* ERROR cannot range over a2.*no structural type */ {} + + var p0 *[10]int + for range p0 {} + for _ = range p0 {} + for _, _ = range p0 {} + + var p1 P1 + for range p1 {} + for _ = range p1 {} + for _, _ = range p1 {} + + var p2 P2 + for range p2 /* ERROR cannot range over p2.*no structural type */ {} + + var m0 map[string]int + for range m0 {} + for _ = range m0 {} + for _, _ = range m0 {} + + var m1 M1 + for range m1 {} + for _ = range m1 {} + for _, _ = range m1 {} + + var m2 M2 + for range m2 /* ERROR cannot range over m2.*no structural type */ {} } // type inference checks var _ = new /* ERROR cannot infer T */ () -func f4[A, B, C any](A, B) C +func f4[A, B, C any](A, B) C { panic(0) } var _ = f4 /* ERROR cannot infer C */ (1, 2) var _ = f4[int, float32, complex128](1, 2) -func f5[A, B, C any](A, []*B, struct{f []C}) int +func f5[A, B, C any](A, []*B, struct{f []C}) int { panic(0) } var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{}) var _ = f5 /* ERROR cannot infer */ (0, nil, struct{f []complex128}{}) var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{}) -func f6[A any](A, []A) int +func f6[A any](A, []A) int { panic(0) } var _ = f6(0, nil) -func f6nil[A any](A) int +func f6nil[A any](A) int { panic(0) } var _ = f6nil /* ERROR cannot infer */ (nil) // type inference with variadic functions -func f7[T any](...T) T +func f7[T any](...T) T { panic(0) } var _ int = f7 /* ERROR cannot infer T */ () var _ int = f7(1) @@ -214,7 +310,7 @@ var _ = f7(float64(1), 2.3) var _ = f7(1, 2.3 /* ERROR does not match */ ) var _ = f7(1.2, 3 /* ERROR does not match */ ) -func f8[A, B any](A, B, ...B) int +func f8[A, B any](A, B, ...B) int { panic(0) } var _ = f8(1) /* ERROR not enough arguments */ var _ = f8(1, 2.3) @@ -227,8 +323,8 @@ var _ = f8[int, float64](0, 0, nil...) // test case for #18268 // init functions cannot have type parameters func init() {} -func init[/* ERROR func init must have no type parameters */ _ any]() {} -func init[/* ERROR func init must have no type parameters */ P any]() {} +func init[_ /* ERROR func init must have no type parameters */ any]() {} +func init[P /* ERROR func init must have no type parameters */ any]() {} type T struct {} @@ -240,7 +336,7 @@ func (T) m3[ /* ERROR methods cannot have type parameters */ P any]() {} type S1[P any] struct { f P } -func f9[P any](x S1[P]) +func f9[P any](x S1[P]) {} func _() { f9[int](S1[int]{42}) @@ -249,7 +345,7 @@ func _() { type S2[A, B, C any] struct{} -func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) +func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) {} func _[P any]() { f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{}) @@ -260,7 +356,7 @@ func _[P any]() { // corner case for type inference // (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) -func f11[T any]() +func f11[T any]() {} func _() { f11[int]() @@ -268,15 +364,16 @@ func _() { // the previous example was extracted from -func f12[T interface{m() T}]() - -type A[T any] T - -func (a A[T]) m() A[T] - -func _[T any]() { - f12[A[T]]() -} +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// func f12[T interface{m() T}]() {} +// +// type A[T any] T +// +// func (a A[T]) m() A[T] +// +// func _[T any]() { +// f12[A[T]]() +// } // method expressions @@ -296,15 +393,15 @@ func _[T any] (x T) { 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{} func (_ R1[A, B]) m0(A, B) -func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T +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]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B) {} func _() { var r R1[int, string] @@ -388,8 +485,8 @@ func (_ R2[X, Y]) m2(X) Y // type assertions and type switches over generic types lead to errors for now func _[T any](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { + _ = x /* ERROR cannot use type assertion */ .(int) + switch x /* ERROR cannot use type switch */ .(type) { } // work-around @@ -399,9 +496,9 @@ func _[T any](x T) { } } -func _[T interface{type int}](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { +func _[T interface{~int}](x T) { + _ = x /* ERROR cannot use type assertion */ .(int) + switch x /* ERROR cannot use type switch */ .(type) { } // work-around @@ -421,13 +518,13 @@ func _[P C[P]] (x P) { type I interface {} func _[P I] (x P) { - x.m /* ERROR interface I has no method m */ () + x.m /* ERROR type P has no field or method m */ () } func _[P interface{}] (x P) { - x.m /* ERROR type bound for P has no method m */ () + x.m /* ERROR type P has no field or method m */ () } func _[P any] (x P) { - x.m /* ERROR type bound for P has no method m */ () + x.m /* ERROR type P has no field or method m */ () } diff --git a/libgo/go/go/types/testdata/check/vardecl.src b/libgo/go/go/types/testdata/check/vardecl.src index 54f5ef1..56abf97 100644 --- a/libgo/go/go/types/testdata/check/vardecl.src +++ b/libgo/go/go/types/testdata/check/vardecl.src @@ -169,13 +169,13 @@ func _() { func _() { var x int - return x /* ERROR no result values expected */ - return math /* ERROR no result values expected */ .Sin(0) + return x /* ERROR too many return values */ + return math /* ERROR too many return values */ .Sin(0) } func _() int { var x, y int - return /* ERROR wrong number of return values */ x, y + return x, y /* ERROR too many return values */ } // Short variable declarations must declare at least one new non-blank variable. diff --git a/libgo/go/go/types/testdata/examples/functions.go2 b/libgo/go/go/types/testdata/examples/functions.go2 index a053471..0af7726 100644 --- a/libgo/go/go/types/testdata/examples/functions.go2 +++ b/libgo/go/go/types/testdata/examples/functions.go2 @@ -66,7 +66,7 @@ var _ float64 = foo(42, []float64{1.0}, &s) // Type inference works in a straight-forward manner even // for variadic functions. -func variadic[A, B any](A, B, ...B) int +func variadic[A, B any](A, B, ...B) int { panic(0) } // var _ = variadic(1) // ERROR not enough arguments var _ = variadic(1, 2.3) @@ -98,7 +98,7 @@ func g2b[P, Q any](x P, y Q) { // Here's an example of a recursive function call with variadic // arguments and type inference inferring the type parameter of // the caller (i.e., itself). -func max[T interface{ type int }](x ...T) T { +func max[T interface{ ~int }](x ...T) T { var x0 T if len(x) > 0 { x0 = x[0] @@ -118,9 +118,9 @@ func max[T interface{ type int }](x ...T) T { // Thus even if a type can be inferred successfully, the function // call may not be valid. -func fboth[T any](chan T) -func frecv[T any](<-chan T) -func fsend[T any](chan<- T) +func fboth[T any](chan T) {} +func frecv[T any](<-chan T) {} +func fsend[T any](chan<- T) {} func _() { var both chan int @@ -140,9 +140,9 @@ func _() { fsend(send) } -func ffboth[T any](func(chan T)) -func ffrecv[T any](func(<-chan T)) -func ffsend[T any](func(chan<- T)) +func ffboth[T any](func(chan T)) {} +func ffrecv[T any](func(<-chan T)) {} +func ffsend[T any](func(chan<- T)) {} func _() { var both func(chan int) @@ -169,9 +169,9 @@ func _() { // assignment is permitted, parameter passing is permitted as well, // so type inference should be able to handle these cases well. -func g1[T any]([]T) -func g2[T any]([]T, T) -func g3[T any](*T, ...T) +func g1[T any]([]T) {} +func g2[T any]([]T, T) {} +func g3[T any](*T, ...T) {} func _() { type intSlize []int @@ -194,7 +194,7 @@ func _() { // Here's a realistic example. -func append[T any](s []T, t ...T) []T +func append[T any](s []T, t ...T) []T { panic(0) } func _() { var f func() @@ -207,8 +207,12 @@ func _() { // (that would indicate a slice type). Thus, generic functions cannot // have empty type parameter lists, either. This is a syntax error. -func h[] /* ERROR empty type parameter list */ () +func h[] /* ERROR empty type parameter list */ () {} func _() { h /* ERROR cannot index */ [] /* ERROR operand */ () } + +// Parameterized functions must have a function body. + +func _ /* ERROR missing function body */ [P any]() diff --git a/libgo/go/go/types/testdata/examples/inference.go2 b/libgo/go/go/types/testdata/examples/inference.go2 index b4f3369..ffa30ee 100644 --- a/libgo/go/go/types/testdata/examples/inference.go2 +++ b/libgo/go/go/types/testdata/examples/inference.go2 @@ -7,10 +7,10 @@ package p type Ordered interface { - type int, float64, string + ~int|~float64|~string } -func min[T Ordered](x, y T) T +func min[T Ordered](x, y T) T { panic(0) } func _() { // min can be called with explicit instantiation. @@ -37,7 +37,7 @@ func _() { _ = min("foo", "bar") } -func mixed[T1, T2, T3 any](T1, T2, T3) +func mixed[T1, T2, T3 any](T1, T2, T3) {} func _() { // mixed can be called with explicit instantiation. @@ -54,7 +54,7 @@ func _() { mixed[int, string](1.1 /* ERROR cannot use 1.1 */ , "", false) } -func related1[Slice interface{type []Elem}, Elem any](s Slice, e Elem) +func related1[Slice interface{~[]Elem}, Elem any](s Slice, e Elem) {} func _() { // related1 can be called with explicit instantiation. @@ -78,7 +78,7 @@ func _() { related1(si, "foo" /* ERROR cannot use "foo" */ ) } -func related2[Elem any, Slice interface{type []Elem}](e Elem, s Slice) +func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {} func _() { // related2 can be called with explicit instantiation. @@ -97,5 +97,28 @@ func _() { // last. related2(1.2, []float64{}) related2(1.0, []int{}) - related2 /* ERROR does not satisfy */ (float64(1.0), []int{}) + related2 /* ERROR does not implement */ (float64(1.0), []int{}) // TODO(gri) fix error position +} + +type List[P any] []P + +func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil } + +func _() { + // related3 can be instantiated explicitly + related3[int, []int]() + related3[byte, List[byte]]() + + // Alternatively, the 2nd type argument can be inferred + // from the first one through constraint type inference. + related3[int]() + + // The inferred type is the structural type of the Slice + // type parameter. + var _ []int = related3[int]() + + // It is not the defined parameterized type List. + type anotherList []float32 + var _ anotherList = related3[float32]() // valid + var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]() } diff --git a/libgo/go/go/types/testdata/examples/methods.go2 b/libgo/go/go/types/testdata/examples/methods.go2 index 76c6539..1d76d55 100644 --- a/libgo/go/go/types/testdata/examples/methods.go2 +++ b/libgo/go/go/types/testdata/examples/methods.go2 @@ -94,3 +94,19 @@ func (_ T2[_, _, _]) _() int { return 42 } type T0 struct{} func (T0) _() {} func (T1[A]) _() {} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // A generic receiver type may constrain its type parameter such +// // that it must be a pointer type. Such receiver types are not +// // permitted. +// type T3a[P interface{ ~int | ~string | ~float64 }] P +// +// func (T3a[_]) m() {} // this is ok +// +// type T3b[P interface{ ~unsafe.Pointer }] P +// +// func (T3b /* ERROR invalid receiver */ [_]) m() {} +// +// type T3c[P interface{ *int | *string }] P +// +// func (T3c /* ERROR invalid receiver */ [_]) m() {} diff --git a/libgo/go/go/types/testdata/examples/types.go2 b/libgo/go/go/types/testdata/examples/types.go2 index 59c8804..33642fa 100644 --- a/libgo/go/go/types/testdata/examples/types.go2 +++ b/libgo/go/go/types/testdata/examples/types.go2 @@ -102,6 +102,7 @@ func _() { // Generic types cannot be used without instantiation. var _ T // ERROR cannot use generic type T +var _ = T /* ERROR cannot use generic type T */ (0) // In type context, generic (parameterized) types cannot be parenthesized before // being instantiated. See also NOTES entry from 12/4/2019. @@ -113,7 +114,7 @@ type I1[T any] interface{ } // There is no such thing as a variadic generic type. -type _[T ... /* ERROR invalid use of ... */ interface{}] struct{} +type _[T ... /* ERROR invalid use of ... */ any] struct{} // Generic interfaces may be embedded as one would expect. type I2 interface { @@ -161,31 +162,42 @@ type _ struct { * /* ERROR List redeclared */ List[int] } +// Issue #45639: We don't allow this anymore. Keep this code +// in case we decide to revisit this decision. +// // It's possible to declare local types whose underlying types // are type parameters. As with ordinary type definitions, the // types underlying properties are "inherited" but the methods // are not. -func _[T interface{ m(); type int }]() { - type L T - var x L - - // m is not defined on L (it is not "inherited" from - // its underlying type). - x.m /* ERROR x.m undefined */ () - - // But the properties of T, such that as that it supports - // the operations of the types given by its type bound, - // are also the properties of L. - x++ - _ = x - x - - // On the other hand, if we define a local alias for T, - // that alias stands for T as expected. - type A = T - var y A - y.m() - _ = y < 0 -} +//func _[T interface{ m(); ~int }]() { +// type L T +// var x L +// +// // m is not defined on L (it is not "inherited" from +// // its underlying type). +// x.m /* ERROR x.m undefined */ () +// +// // But the properties of T, such that as that it supports +// // the operations of the types given by its type bound, +// // are also the properties of L. +// x++ +// _ = x - x +// +// // On the other hand, if we define a local alias for T, +// // that alias stands for T as expected. +// type A = T +// var y A +// y.m() +// _ = y < 0 +//} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // It is not permitted to declare a local type whose underlying +// // type is a type parameter not declared by that type declaration. +// func _[T any]() { +// type _ T // ERROR cannot use function type parameter T as RHS in type declaration +// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration +// } // As a special case, an explicit type argument may be omitted // from a type parameter bound if the type bound expects exactly @@ -208,19 +220,19 @@ func Sum[T Adder[T]](list []T) T { } // Valid and invalid variations. -type B0 interface {} -type B1[_ any] interface{} -type B2[_, _ any] interface{} +type B0 any +type B1[_ any] any +type B2[_, _ any] any -func _[T1 B0]() -func _[T1 B1[T1]]() -func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() +func _[T1 B0]() {} +func _[T1 B1[T1]]() {} +func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {} -func _[T1, T2 B0]() -func _[T1 B1[T1], T2 B1[T2]]() -func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() +func _[T1, T2 B0]() {} +func _[T1 B1[T1], T2 B1[T2]]() {} +func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {} -func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2 +func _[T1 B0, T2 B1[T2]]() {} // here B1 applies to T2 // When the type argument is left away, the type bound is // instantiated for each type parameter with that type @@ -238,11 +250,11 @@ func _[A Adder[A], B Adder[B], C Adder[A]]() { // The type of variables (incl. parameters and return values) cannot // be an interface with type constraints or be/embed comparable. type I interface { - type int + ~int } var ( - _ interface /* ERROR contains type constraints */ {type int} + _ interface /* ERROR contains type constraints */ {~int} _ I /* ERROR contains type constraints */ ) @@ -270,10 +282,10 @@ func _() { // Type parameters are never const types, i.e., it's // not possible to declare a constant of type parameter type. -// (If a type list contains just a single const type, we could -// allow it, but such type lists don't make much sense in the +// (If a type set contains just a single const type, we could +// allow it, but such type sets don't make much sense in the // first place.) -func _[T interface { type int, float64 }]() { +func _[T interface {~int|~float64}]() { // not valid const _ = T /* ERROR not constant */ (0) const _ T /* ERROR invalid constant type T */ = 1 @@ -284,3 +296,26 @@ func _[T interface { type int, float64 }]() { _ = T(0) } +// It is possible to create composite literals of type parameter +// type as long as it's possible to create a composite literal +// of the structural type of the type parameter's constraint. +func _[P interface{ ~[]int }]() P { + return P{} + return P{1, 2, 3} +} + +func _[P interface{ ~[]E }, E interface{ map[string]P } ]() P { + x := P{} + return P{{}} + return P{E{}} + return P{E{"foo": x}} + return P{{"foo": x}, {}} +} + +// This is a degenerate case with a singleton type set, but we can create +// composite literals even if the structural type is a defined type. +type MyInts []int + +func _[P MyInts]() P { + return P{} +} diff --git a/libgo/go/go/types/testdata/fixedbugs/issue25838.go b/libgo/go/go/types/testdata/fixedbugs/issue25838.go new file mode 100644 index 0000000..adbd138 --- /dev/null +++ b/libgo/go/go/types/testdata/fixedbugs/issue25838.go @@ -0,0 +1,26 @@ +// 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 + +// examples from the issue + +type ( + e = f + f = g + g = []h + h i + i = j + j = e +) + +type ( + e1 = []h1 + h1 e1 +) + +type ( + P = *T + T P +) diff --git a/libgo/go/go/types/testdata/fixedbugs/issue28251.src b/libgo/go/go/types/testdata/fixedbugs/issue28251.src index cd79e0e..ef5e61d 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue28251.src +++ b/libgo/go/go/types/testdata/fixedbugs/issue28251.src @@ -60,6 +60,6 @@ type ( T11 = T ) -func (T9 /* ERROR invalid receiver \*\*T */ ) m9() {} +func (T9 /* ERROR invalid receiver type \*\*T */ ) m9() {} func _() { (T{}).m9 /* ERROR has no field or method m9 */ () } func _() { (&T{}).m9 /* ERROR has no field or method m9 */ () } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39634.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39634.go2 index a13ed13..34ab654 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39634.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39634.go2 @@ -31,13 +31,14 @@ type x7[A any] struct{ foo7 } func main7() { var _ foo7 = x7[int]{} } // crash 8 -type foo8[A any] interface { type A } -func bar8[A foo8[A]](a A) {} -func main8() {} +// Embedding stand-alone type parameters is not permitted for now. Disabled. +// type foo8[A any] interface { ~A } +// func bar8[A foo8[A]](a A) {} +// func main8() {} // crash 9 -type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] } -func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) } +type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] } +func _() { var _ = new(foo9[int]) } // crash 12 var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len /* ERROR must be called */ /* ERROR must be called */ )]c /* ERROR undeclared */ /* ERROR undeclared */ @@ -49,7 +50,7 @@ func (G15 /* ERROR generic type .* without instantiation */ ) p() // crash 16 type Foo16[T any] r16 /* ERROR not a type */ -func r16[T any]() Foo16[Foo16[T]] +func r16[T any]() Foo16[Foo16[T]] { panic(0) } // crash 17 type Y17 interface{ c() } @@ -57,7 +58,7 @@ type Z17 interface { c() Y17 Y17 /* ERROR duplicate method */ } -func F17[T Z17](T) +func F17[T Z17](T) {} // crash 18 type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ]) @@ -73,9 +74,10 @@ func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) } type Z21 /* ERROR illegal cycle */ interface{ Z21 } func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) } -// crash 24 -type T24[P any] P -func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() } +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // crash 24 +// type T24[P any] P +// func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() } // crash 25 type T25[A any] int @@ -84,8 +86,11 @@ var x T25 /* ERROR without instantiation */ .m1 // crash 26 type T26 = interface{ F26[ /* ERROR methods cannot have 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[]. func F26[Z any]() T26 { return F26[] /* ERROR operand */ } // crash 27 -func e27[T any]() interface{ x27 /* ERROR not a type */ } +func e27[T any]() interface{ x27 /* ERROR not a type */ } { panic(0) } func x27() { e27 /* ERROR cannot infer T */ () } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39680.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39680.go2 index 9bc26f3..e56bc35 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39680.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39680.go2 @@ -4,16 +4,19 @@ package p +// Embedding stand-alone type parameters is not permitted for now. Disabled. + +/* import "fmt" // Minimal test case. -func _[T interface{type T}](x T) T{ +func _[T interface{~T}](x T) T{ return x } // Test case from issue. type constr[T any] interface { - type T + ~T } func Print[T constr[T]](s []T) { @@ -25,3 +28,4 @@ func Print[T constr[T]](s []T) { func f() { Print([]string{"Hello, ", "playground\n"}) } +*/ diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39693.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39693.go2 index 316ab19..ec76419 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39693.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39693.go2 @@ -4,11 +4,20 @@ package p -type Number interface { - int /* ERROR int is not an interface */ - float64 /* ERROR float64 is not an interface */ +type Number1 interface { + // embedding non-interface types is permitted + int + float64 } -func Add[T Number](a, b T) T { +func Add[T Number1](a, b T) T { return a /* ERROR not defined */ + b } + +type Number2 interface { + int|float64 +} + +func Add2[T Number2](a, b T) T { + return a + b +} diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39699.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39699.go2 index 75491e7..72f8399 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39699.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39699.go2 @@ -8,7 +8,7 @@ type T0 interface{ } type T1 interface{ - type int + ~int } type T2 interface{ diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39711.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39711.go2 index df621a4..d85fa03 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39711.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39711.go2 @@ -4,8 +4,10 @@ package p -// Do not report a duplicate type error for this type list. +// Do not report a duplicate type error for this term list. // (Check types after interfaces have been completed.) type _ interface { - type interface{ Error() string }, interface{ String() string } + // TODO(rfindley) Once we have full type sets we can enable this again. + // Fow now we don't permit interfaces in term lists. + // type interface{ Error() string }, interface{ String() string } } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39723.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39723.go2 index 55464e6..0088523 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39723.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39723.go2 @@ -6,4 +6,4 @@ package p // A constraint must be an interface; it cannot // be a type parameter, for instance. -func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]() +func _[A interface{ ~int }, B A /* ERROR cannot use a type parameter as constraint */ ]() {} diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39725.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39725.go2 index e19b677..62dc45a 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39725.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39725.go2 @@ -4,13 +4,13 @@ package p -func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) +func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) {} func _() { f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{}) } // simplified test case from issue -func f2[T any](_ []T, _ func(T)) +func f2[T any](_ []T, _ func(T)) {} func _() { f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {}) } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39754.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39754.go2 index b2ba9de..5c9c4e6 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39754.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39754.go2 @@ -16,9 +16,6 @@ func f[V interface{}, A, B Box[V]]() {} func _() { f[int, Optional[int], Optional[int]]() - _ = f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]] - // TODO(gri) Provide better position information here. - // See TODO in call.go, Checker.arguments. - // TODO(rFindley) Reconcile this error position with types2. - f /* ERROR does not satisfy Box */ [int, Optional[int], Optional[string]]() + _ = f[int, Optional[int], Optional /* ERROR does not implement Box */ [string]] + _ = f[int, Optional[int], Optional /* ERROR Optional.* does not implement Box.* */ [string]] } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39755.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39755.go2 index b7ab688..257b73a 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39755.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39755.go2 @@ -4,14 +4,14 @@ package p -func _[T interface{type map[string]int}](x T) { +func _[T interface{~map[string]int}](x T) { _ = x == nil } // simplified test case from issue type PathParamsConstraint interface { - type map[string]string, []struct{key, value string} + ~map[string]string | ~[]struct{key, value string} } type PathParams[T PathParamsConstraint] struct { diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39768.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39768.go2 index abac141..696d9d9 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39768.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39768.go2 @@ -4,17 +4,18 @@ package p -type T[P any] P -type A = T -var x A[int] -var _ A /* ERROR cannot use generic type */ - -type B = T[int] -var y B = x -var _ B /* ERROR not a generic type */ [int] +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type T[P any] P +// type A = T // ERROR cannot use generic type +// var x A[int] +// var _ A +// +// type B = T[int] +// var y B = x +// var _ B /* ERROR not a generic type */ [int] // test case from issue type Vector[T any] []T -type VectorAlias = Vector +type VectorAlias = Vector // ERROR cannot use generic type var v Vector[int] diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39938.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39938.go2 index 76e7e36..6bc9284 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39938.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39938.go2 @@ -2,20 +2,20 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Check "infinite expansion" cycle errors across instantiated types. - package p -type E0[P any] P +// All but E2 and E5 provide an "indirection" and break infinite expansion of a type. +type E0[P any] []P type E1[P any] *P -type E2[P any] struct{ P } -type E3[P any] struct{ *P } +type E2[P any] struct{ _ P } +type E3[P any] struct{ _ *P } +type E5[P any] struct{ _ [10]P } -type T0 /* ERROR illegal cycle */ struct { +type T0 struct { _ E0[T0] } -type T0_ /* ERROR illegal cycle */ struct { +type T0_ struct { E0[T0_] } @@ -31,20 +31,24 @@ type T3 struct { _ E3[T3] } -// some more complex cases - -type T4 /* ERROR illegal cycle */ struct { - _ E0[E2[T4]] -} +type T4 /* ERROR illegal cycle */ [10]E5[T4] type T5 struct { - _ E0[E2[E0[E1[E2[[10]T5]]]]] + _ E0[E2[T5]] } -type T6 /* ERROR illegal cycle */ struct { - _ E0[[10]E2[E0[E2[E2[T6]]]]] +type T6 struct { + _ E0[E2[E0[E1[E2[[10]T6]]]]] } type T7 struct { - _ E0[[]E2[E0[E2[E2[T6]]]]] + _ E0[[10]E2[E0[E2[E2[T7]]]]] } + +type T8 struct { + _ E0[[]E2[E0[E2[E2[T8]]]]] +} + +type T9 /* ERROR illegal cycle */ [10]E2[E5[E2[T9]]] + +type T10 [10]E2[E5[E2[func(T10)]]] diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39948.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39948.go2 index c2b4609..e38e572 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39948.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39948.go2 @@ -5,5 +5,5 @@ package p type T[P any] interface{ - P // ERROR P is a type parameter, not an interface + P // ERROR cannot embed a type parameter } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue39976.go2 b/libgo/go/go/types/testdata/fixedbugs/issue39976.go2 index 3db4eae..d703da9 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue39976.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue39976.go2 @@ -7,7 +7,7 @@ package p type policy[K, V any] interface{} type LRU[K, V any] struct{} -func NewCache[K, V any](p policy[K, V]) +func NewCache[K, V any](p policy[K, V]) {} func _() { var lru LRU[int, string] diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40038.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40038.go2 index 8948d61..5f81fcb 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue40038.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue40038.go2 @@ -8,8 +8,8 @@ type A[T any] int func (A[T]) m(A[T]) -func f[P interface{m(P)}]() +func f[P interface{m(P)}]() {} func _() { _ = f[A[int]] -}
\ No newline at end of file +} diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40056.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40056.go2 index f587691..66130c0 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue40056.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue40056.go2 @@ -10,6 +10,6 @@ func _() { type S struct {} -func NewS[T any]() *S +func NewS[T any]() *S { panic(0) } func (_ *S /* ERROR S is not a generic type */ [T]) M() diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40301.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40301.go2 index 5d97855..c78f9a1 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue40301.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue40301.go2 @@ -7,6 +7,6 @@ package p import "unsafe" func _[T any](x T) { - _ = unsafe /* ERROR undefined */ .Alignof(x) - _ = unsafe /* ERROR undefined */ .Sizeof(x) + _ = unsafe.Alignof(x) + _ = unsafe.Sizeof(x) } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue40684.go2 b/libgo/go/go/types/testdata/fixedbugs/issue40684.go2 index 0269c3a..63a058d 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue40684.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue40684.go2 @@ -6,10 +6,10 @@ package p type T[_ any] int -func f[_ any]() -func g[_, _ any]() +func f[_ any]() {} +func g[_, _ any]() {} func _() { _ = f[T /* ERROR without instantiation */ ] _ = g[T /* ERROR without instantiation */ , T /* ERROR without instantiation */ ] -}
\ No newline at end of file +} diff --git a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 index 61f766b..7f55ba8 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 @@ -6,13 +6,13 @@ package p // Test case from issue. -type Nat interface { - type Zero, Succ +type Nat /* ERROR cycle */ interface { + Zero|Succ } type Zero struct{} type Succ struct{ - Nat // ERROR interface contains type constraints + Nat // Nat contains type constraints but is invalid, so no error } // Struct tests. @@ -22,7 +22,7 @@ type I1 interface { } type I2 interface { - type int + ~int } type I3 interface { @@ -47,7 +47,7 @@ type _ struct{ } type _ struct{ - I3 // ERROR interface contains type constraints + I3 // ERROR interface is .* comparable } // General composite types. @@ -59,19 +59,19 @@ type ( _ []I1 // ERROR interface is .* comparable _ []I2 // ERROR interface contains type constraints - _ *I3 // ERROR interface contains type constraints + _ *I3 // ERROR interface is .* comparable _ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints - _ chan I3 // ERROR interface contains type constraints + _ chan I3 // ERROR interface is .* comparable _ func(I1 /* ERROR interface is .* comparable */ ) _ func() I2 // ERROR interface contains type constraints ) // Other cases. -var _ = [...]I3 /* ERROR interface contains type constraints */ {} +var _ = [...]I3 /* ERROR interface is .* comparable */ {} func _(x interface{}) { - _ = x.(I3 /* ERROR interface contains type constraints */ ) + _ = x.(I3 /* ERROR interface is .* comparable */ ) } type T1[_ any] struct{} @@ -79,9 +79,9 @@ type T3[_, _, _ any] struct{} var _ T1[I2 /* ERROR interface contains type constraints */ ] var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32] -func f1[_ any]() int +func f1[_ any]() int { panic(0) } var _ = f1[I2 /* ERROR interface contains type constraints */ ]() -func f3[_, _, _ any]() int +func f3[_, _, _ any]() int { panic(0) } var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]() func _(x interface{}) { diff --git a/libgo/go/go/types/testdata/fixedbugs/issue42758.go2 b/libgo/go/go/types/testdata/fixedbugs/issue42758.go2 index 698cb8a..dd66e96 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue42758.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue42758.go2 @@ -17,7 +17,7 @@ func _[T any](x interface{}){ } type constraint interface { - type int + ~int } func _[T constraint](x interface{}){ @@ -28,6 +28,6 @@ func _[T constraint](x interface{}){ } func _(x constraint /* ERROR contains type constraints */ ) { - switch x /* ERROR contains type constraints */ .(type) { + switch x.(type) { // no need to report another error } } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45114.go b/libgo/go/go/types/testdata/fixedbugs/issue45114.go new file mode 100644 index 0000000..0093660 --- /dev/null +++ b/libgo/go/go/types/testdata/fixedbugs/issue45114.go @@ -0,0 +1,8 @@ +// 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 + +var s uint +var _ = string(1 /* ERROR shifted operand 1 .* must be integer */ << s) diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 index b1e4249..b8ba0ad 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 @@ -4,7 +4,7 @@ package p -func f[F interface{type *Q}, G interface{type *R}, Q, R any](q Q, r R) {} +func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {} func _() { f[*float64, *int](1, 2) diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45635.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45635.go2 index 3e2ccec..fc50797 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue45635.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue45635.go2 @@ -10,10 +10,10 @@ func main() { type N[T any] struct{} -var _ N /* ERROR "0 arguments but 1 type parameters" */ [] +var _ N [] // ERROR expected type argument list type I interface { - type map[int]int, []int + ~[]int } func _[T I](i, j int) { @@ -27,6 +27,5 @@ func _[T I](i, j int) { _ = s[i, j /* ERROR "more than one index" */ ] var t T - // TODO(rFindley) Fix the duplicate error below. - _ = t[i, j /* ERROR "more than one index" */ /* ERROR "more than one index" */ ] + _ = t[i, j /* ERROR "more than one index" */ ] } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45985.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45985.go2 index 550b9c6..9a0f5e3 100644 --- a/libgo/go/go/types/testdata/fixedbugs/issue45985.go2 +++ b/libgo/go/go/types/testdata/fixedbugs/issue45985.go2 @@ -4,11 +4,10 @@ package issue45985 -// TODO(rFindley): this error should be on app[int] below. -func app[S /* ERROR "type S = S does not match" */ interface{ type []T }, T any](s S, e T) S { +func app[S interface{ ~[]T }, T any](s S, e T) S { return append(s, e) } func _() { - _ = app[int] + _ = app/* ERROR "S does not match" */[int] } diff --git a/libgo/go/go/types/testdata/fixedbugs/issue49003.go b/libgo/go/go/types/testdata/fixedbugs/issue49003.go new file mode 100644 index 0000000..ece1a27 --- /dev/null +++ b/libgo/go/go/types/testdata/fixedbugs/issue49003.go @@ -0,0 +1,10 @@ +// 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 + +func f(s string) int { + for range s { + } +} // ERROR missing return diff --git a/libgo/go/go/types/tuple.go b/libgo/go/go/types/tuple.go new file mode 100644 index 0000000..e85c5aa --- /dev/null +++ b/libgo/go/go/types/tuple.go @@ -0,0 +1,34 @@ +// Copyright 2011 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 types + +// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. +// Tuples are used as components of signatures and to represent the type of multiple +// assignments; they are not first class types of Go. +type Tuple struct { + vars []*Var +} + +// NewTuple returns a new tuple for the given variables. +func NewTuple(x ...*Var) *Tuple { + if len(x) > 0 { + return &Tuple{vars: x} + } + return nil +} + +// Len returns the number variables of tuple t. +func (t *Tuple) Len() int { + if t != nil { + return len(t.vars) + } + return 0 +} + +// At returns the i'th variable of tuple t. +func (t *Tuple) At(i int) *Var { return t.vars[i] } + +func (t *Tuple) Underlying() Type { return t } +func (t *Tuple) String() string { return TypeString(t, nil) } diff --git a/libgo/go/go/types/type.go b/libgo/go/go/types/type.go index 2660ce4..3acb19c 100644 --- a/libgo/go/go/types/type.go +++ b/libgo/go/go/types/type.go @@ -4,983 +4,123 @@ package types -import ( - "fmt" - "go/token" - "sync/atomic" -) - // A Type represents a type of Go. // All types implement the Type interface. type Type interface { // Underlying returns the underlying type of a type // w/o following forwarding chains. Only used by - // client packages (here for backward-compatibility). + // client packages. Underlying() Type // String returns a string representation of a type. String() string } -// BasicKind describes the kind of basic type. -type BasicKind int - -const ( - Invalid BasicKind = iota // type is invalid - - // predeclared types - Bool - Int - Int8 - Int16 - Int32 - Int64 - Uint - Uint8 - Uint16 - Uint32 - Uint64 - Uintptr - Float32 - Float64 - Complex64 - Complex128 - String - UnsafePointer - - // types for untyped values - UntypedBool - UntypedInt - UntypedRune - UntypedFloat - UntypedComplex - UntypedString - UntypedNil - - // aliases - Byte = Uint8 - Rune = Int32 -) - -// BasicInfo is a set of flags describing properties of a basic type. -type BasicInfo int - -// Properties of basic types. -const ( - IsBoolean BasicInfo = 1 << iota - IsInteger - IsUnsigned - IsFloat - IsComplex - IsString - IsUntyped - - IsOrdered = IsInteger | IsFloat | IsString - IsNumeric = IsInteger | IsFloat | IsComplex - IsConstType = IsBoolean | IsNumeric | IsString -) - -// A Basic represents a basic type. -type Basic struct { - kind BasicKind - info BasicInfo - name string -} - -// Kind returns the kind of basic type b. -func (b *Basic) Kind() BasicKind { return b.kind } - -// Info returns information about properties of basic type b. -func (b *Basic) Info() BasicInfo { return b.info } - -// Name returns the name of basic type b. -func (b *Basic) Name() string { return b.name } - -// An Array represents an array type. -type Array struct { - len int64 - elem Type -} - -// NewArray returns a new array type for the given element type and length. -// A negative length indicates an unknown length. -func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} } - -// Len returns the length of array a. -// A negative result indicates an unknown length. -func (a *Array) Len() int64 { return a.len } - -// Elem returns element type of array a. -func (a *Array) Elem() Type { return a.elem } - -// A Slice represents a slice type. -type Slice struct { - elem Type -} - -// NewSlice returns a new slice type for the given element type. -func NewSlice(elem Type) *Slice { return &Slice{elem: elem} } - -// Elem returns the element type of slice s. -func (s *Slice) Elem() Type { return s.elem } - -// A Struct represents a struct type. -type Struct struct { - fields []*Var - tags []string // field tags; nil if there are no tags -} - -// NewStruct returns a new struct with the given fields and corresponding field tags. -// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be -// only as long as required to hold the tag with the largest index i. Consequently, -// if no field has a tag, tags may be nil. -func NewStruct(fields []*Var, tags []string) *Struct { - var fset objset - for _, f := range fields { - if f.name != "_" && fset.insert(f) != nil { - panic("multiple fields with the same name") - } - } - if len(tags) > len(fields) { - panic("more tags than fields") - } - return &Struct{fields: fields, tags: tags} -} - -// NumFields returns the number of fields in the struct (including blank and embedded fields). -func (s *Struct) NumFields() int { return len(s.fields) } - -// Field returns the i'th field for 0 <= i < NumFields(). -func (s *Struct) Field(i int) *Var { return s.fields[i] } - -// Tag returns the i'th field tag for 0 <= i < NumFields(). -func (s *Struct) Tag(i int) string { - if i < len(s.tags) { - return s.tags[i] - } - return "" -} - -// A Pointer represents a pointer type. -type Pointer struct { - base Type // element type -} - -// NewPointer returns a new pointer type for the given element (base) type. -func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } - -// Elem returns the element type for the given pointer p. -func (p *Pointer) Elem() Type { return p.base } - -// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. -// Tuples are used as components of signatures and to represent the type of multiple -// assignments; they are not first class types of Go. -type Tuple struct { - vars []*Var -} - -// NewTuple returns a new tuple for the given variables. -func NewTuple(x ...*Var) *Tuple { - if len(x) > 0 { - return &Tuple{vars: x} - } - // TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer; - // it's too subtle and causes problems. - return nil -} - -// Len returns the number variables of tuple t. -func (t *Tuple) Len() int { - if t != nil { - return len(t.vars) - } - return 0 -} - -// At returns the i'th variable of tuple t. -func (t *Tuple) At(i int) *Var { return t.vars[i] } - -// A Signature represents a (non-builtin) function or method type. -// The receiver is ignored when comparing signatures for identity. -type Signature struct { - // We need to keep the scope in Signature (rather than passing it around - // and store it in the Func Object) because when type-checking a function - // literal we call the general type checker which returns a general Type. - // We then unpack the *Signature and use the scope for the literal body. - rparams []*TypeName // receiver type parameters from left to right, or nil - tparams []*TypeName // type parameters from left to right, or nil - scope *Scope // function scope, present for package-local signatures - recv *Var // nil if not a method - params *Tuple // (incoming) parameters from left to right; or nil - results *Tuple // (outgoing) results from left to right; or nil - variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) -} - -// NewSignature returns a new function type for the given receiver, parameters, -// and results, either of which may be nil. If variadic is set, the function -// is variadic, it must have at least one parameter, and the last parameter -// must be of unnamed slice type. -func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { - if variadic { - n := params.Len() - if n == 0 { - panic("types.NewSignature: variadic function must have at least one parameter") - } - if _, ok := params.At(n - 1).typ.(*Slice); !ok { - panic("types.NewSignature: variadic parameter must be of unnamed slice type") - } - } - return &Signature{recv: recv, params: params, results: results, variadic: variadic} -} - -// Recv returns the receiver of signature s (if a method), or nil if a -// function. It is ignored when comparing signatures for identity. -// -// For an abstract method, Recv returns the enclosing interface either -// as a *Named or an *Interface. Due to embedding, an interface may -// contain methods whose receiver type is a different interface. -func (s *Signature) Recv() *Var { return s.recv } - -// _TParams returns the type parameters of signature s, or nil. -func (s *Signature) _TParams() []*TypeName { return s.tparams } - -// _SetTParams sets the type parameters of signature s. -func (s *Signature) _SetTParams(tparams []*TypeName) { s.tparams = tparams } - -// Params returns the parameters of signature s, or nil. -func (s *Signature) Params() *Tuple { return s.params } - -// Results returns the results of signature s, or nil. -func (s *Signature) Results() *Tuple { return s.results } - -// Variadic reports whether the signature s is variadic. -func (s *Signature) Variadic() bool { return s.variadic } - -// A _Sum represents a set of possible types. -// Sums are currently used to represent type lists of interfaces -// and thus the underlying types of type parameters; they are not -// first class types of Go. -type _Sum struct { - types []Type // types are unique -} - -// _NewSum returns a new Sum type consisting of the provided -// types if there are more than one. If there is exactly one -// type, it returns that type. If the list of types is empty -// the result is nil. -func _NewSum(types []Type) Type { - if len(types) == 0 { - return nil - } - - // What should happen if types contains a sum type? - // Do we flatten the types list? For now we check - // and panic. This should not be possible for the - // current use case of type lists. - // TODO(gri) Come up with the rules for sum types. - for _, t := range types { - if _, ok := t.(*_Sum); ok { - panic("sum type contains sum type - unimplemented") - } - } - - if len(types) == 1 { - return types[0] - } - return &_Sum{types: types} -} - -// is reports whether all types in t satisfy pred. -func (s *_Sum) is(pred func(Type) bool) bool { - if s == nil { - return false - } - for _, t := range s.types { - if !pred(t) { - return false - } +// under returns the true expanded underlying type. +// If it doesn't exist, the result is Typ[Invalid]. +// under must only be called when a type is known +// to be fully set up. +func under(t Type) Type { + if t, _ := t.(*Named); t != nil { + return t.under() } - return true -} - -// An Interface represents an interface type. -type Interface struct { - methods []*Func // ordered list of explicitly declared methods - types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name) - embeddeds []Type // ordered list of explicitly embedded types - - allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) - allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name) - - obj Object // type declaration defining this interface; or nil (for better error messages) + return t.Underlying() } -// unpack unpacks a type into a list of types. -// TODO(gri) Try to eliminate the need for this function. -func unpackType(typ Type) []Type { - if typ == nil { - return nil - } - if sum := asSum(typ); sum != nil { - return sum.types +// If t is not a type parameter, structuralType returns the underlying type. +// If t is a type parameter, structuralType returns the single underlying +// type of all types in its type set if it exists, or nil otherwise. If the +// type set contains only unrestricted and restricted channel types (with +// identical element types), the single underlying type is the restricted +// channel type if the restrictions are always the same, or nil otherwise. +func structuralType(t Type) Type { + tpar, _ := t.(*TypeParam) + if tpar == nil { + return under(t) } - return []Type{typ} -} -// is reports whether interface t represents types that all satisfy pred. -func (t *Interface) is(pred func(Type) bool) bool { - if t.allTypes == nil { - return false // we must have at least one type! (was bug) - } - for _, t := range unpackType(t.allTypes) { - if !pred(t) { + var su Type + if tpar.underIs(func(u Type) bool { + if u == nil { return false } - } - return true -} - -// emptyInterface represents the empty (completed) interface -var emptyInterface = Interface{allMethods: markComplete} - -// markComplete is used to mark an empty interface as completely -// set up by setting the allMethods field to a non-nil empty slice. -var markComplete = make([]*Func, 0) - -// NewInterface returns a new (incomplete) interface for the given methods and embedded types. -// Each embedded type must have an underlying type of interface type. -// NewInterface takes ownership of the provided methods and may modify their types by setting -// missing receivers. To compute the method set of the interface, Complete must be called. -// -// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types -// to be embedded. This is necessary for interfaces that embed alias type names referring to -// non-defined (literal) interface types. -func NewInterface(methods []*Func, embeddeds []*Named) *Interface { - tnames := make([]Type, len(embeddeds)) - for i, t := range embeddeds { - tnames[i] = t - } - return NewInterfaceType(methods, tnames) -} - -// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types. -// Each embedded type must have an underlying type of interface type (this property is not -// verified for defined types, which may be in the process of being set up and which don't -// have a valid underlying type yet). -// NewInterfaceType takes ownership of the provided methods and may modify their types by setting -// missing receivers. To compute the method set of the interface, Complete must be called. -func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface { - if len(methods) == 0 && len(embeddeds) == 0 { - return &emptyInterface - } - - // set method receivers if necessary - typ := new(Interface) - for _, m := range methods { - if sig := m.typ.(*Signature); sig.recv == nil { - sig.recv = NewVar(m.pos, m.pkg, "", typ) - } - } - - // All embedded types should be interfaces; however, defined types - // may not yet be fully resolved. Only verify that non-defined types - // are interfaces. This matches the behavior of the code before the - // fix for #25301 (issue #25596). - for _, t := range embeddeds { - if _, ok := t.(*Named); !ok && !IsInterface(t) { - panic("embedded type is not an interface") - } - } - - // sort for API stability - sortMethods(methods) - sortTypes(embeddeds) - - typ.methods = methods - typ.embeddeds = embeddeds - return typ -} - -// NumExplicitMethods returns the number of explicitly declared methods of interface t. -func (t *Interface) NumExplicitMethods() int { return len(t.methods) } - -// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). -// The methods are ordered by their unique Id. -func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } - -// NumEmbeddeds returns the number of embedded types in interface t. -func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } - -// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds(). -// The result is nil if the i'th embedded type is not a defined type. -// -// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types. -func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname } - -// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). -func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] } - -// NumMethods returns the total number of methods of interface t. -// The interface must have been completed. -func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) } - -func (t *Interface) assertCompleteness() { - if t.allMethods == nil { - panic("interface is incomplete") - } -} - -// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). -// The methods are ordered by their unique Id. -// The interface must have been completed. -func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] } - -// Empty reports whether t is the empty interface. -func (t *Interface) Empty() bool { - if t.allMethods != nil { - // interface is complete - quick test - // A non-nil allTypes may still be empty and represents the bottom type. - return len(t.allMethods) == 0 && t.allTypes == nil - } - return !t.iterate(func(t *Interface) bool { - return len(t.methods) > 0 || t.types != nil - }, nil) -} - -// _HasTypeList reports whether interface t has a type list, possibly from an embedded type. -func (t *Interface) _HasTypeList() bool { - if t.allMethods != nil { - // interface is complete - quick test - return t.allTypes != nil - } - - return t.iterate(func(t *Interface) bool { - return t.types != nil - }, nil) -} - -// _IsComparable reports whether interface t is or embeds the predeclared interface "comparable". -func (t *Interface) _IsComparable() bool { - if t.allMethods != nil { - // interface is complete - quick test - _, m := lookupMethod(t.allMethods, nil, "==") - return m != nil - } - - return t.iterate(func(t *Interface) bool { - _, m := lookupMethod(t.methods, nil, "==") - return m != nil - }, nil) -} - -// _IsConstraint reports t.HasTypeList() || t.IsComparable(). -func (t *Interface) _IsConstraint() bool { - if t.allMethods != nil { - // interface is complete - quick test - if t.allTypes != nil { - return true - } - _, m := lookupMethod(t.allMethods, nil, "==") - return m != nil - } - - return t.iterate(func(t *Interface) bool { - if t.types != nil { - return true - } - _, m := lookupMethod(t.methods, nil, "==") - return m != nil - }, nil) -} - -// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true. -// iterate reports whether any call to f returned true. -func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool { - if f(t) { - return true - } - for _, e := range t.embeddeds { - // e should be an interface but be careful (it may be invalid) - if e := asInterface(e); e != nil { - // Cyclic interfaces such as "type E interface { E }" are not permitted - // but they are still constructed and we need to detect such cycles. - if seen[e] { - continue - } - if seen == nil { - seen = make(map[*Interface]bool) - } - seen[e] = true - if e.iterate(f, seen) { - return true + if su != nil { + u = match(su, u) + if u == nil { + return false } } - } - return false -} - -// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ. -// If the type list is empty (absent), typ trivially satisfies the interface. -// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive -// "implements" predicate. -func (t *Interface) isSatisfiedBy(typ Type) bool { - t.Complete() - if t.allTypes == nil { + // su == nil || match(su, u) != nil + su = u return true + }) { + return su } - types := unpackType(t.allTypes) - return includes(types, typ) || includes(types, under(typ)) + return nil } -// Complete computes the interface's method set. It must be called by users of -// NewInterfaceType and NewInterface after the interface's embedded types are -// fully defined and before using the interface type in any way other than to -// form other types. The interface must not contain duplicate methods or a -// panic occurs. Complete returns the receiver. -func (t *Interface) Complete() *Interface { - // TODO(gri) consolidate this method with Checker.completeInterface - if t.allMethods != nil { - return t +// structuralString is like structuralType but also considers []byte +// and strings as identical. In this case, if successful and we saw +// a string, the result is of type (possibly untyped) string. +func structuralString(t Type) Type { + tpar, _ := t.(*TypeParam) + if tpar == nil { + return under(t) // string or untyped string } - t.allMethods = markComplete // avoid infinite recursion - - var todo []*Func - var methods []*Func - var seen objset - addMethod := func(m *Func, explicit bool) { - switch other := seen.insert(m); { - case other == nil: - methods = append(methods, m) - case explicit: - panic("duplicate method " + m.name) - default: - // check method signatures after all locally embedded interfaces are computed - todo = append(todo, m, other.(*Func)) + var su Type + hasString := false + if tpar.underIs(func(u Type) bool { + if u == nil { + return false } - } - - for _, m := range t.methods { - addMethod(m, true) - } - - allTypes := t.types - - for _, typ := range t.embeddeds { - utyp := under(typ) - etyp := asInterface(utyp) - if etyp == nil { - if utyp != Typ[Invalid] { - panic(fmt.Sprintf("%s is not an interface", typ)) - } - continue + if isString(u) { + u = NewSlice(universeByte) + hasString = true } - etyp.Complete() - for _, m := range etyp.allMethods { - addMethod(m, false) + if su != nil { + u = match(su, u) + if u == nil { + return false + } } - allTypes = intersect(allTypes, etyp.allTypes) - } - - for i := 0; i < len(todo); i += 2 { - m := todo[i] - other := todo[i+1] - if !Identical(m.typ, other.typ) { - panic("duplicate method " + m.name) + // su == nil || match(su, u) != nil + su = u + return true + }) { + if hasString { + return Typ[String] } + return su } - - if methods != nil { - sortMethods(methods) - t.allMethods = methods - } - t.allTypes = allTypes - - return t -} - -// A Map represents a map type. -type Map struct { - key, elem Type -} - -// NewMap returns a new map for the given key and element types. -func NewMap(key, elem Type) *Map { - return &Map{key: key, elem: elem} -} - -// Key returns the key type of map m. -func (m *Map) Key() Type { return m.key } - -// Elem returns the element type of map m. -func (m *Map) Elem() Type { return m.elem } - -// A Chan represents a channel type. -type Chan struct { - dir ChanDir - elem Type -} - -// A ChanDir value indicates a channel direction. -type ChanDir int - -// The direction of a channel is indicated by one of these constants. -const ( - SendRecv ChanDir = iota - SendOnly - RecvOnly -) - -// NewChan returns a new channel type for the given direction and element type. -func NewChan(dir ChanDir, elem Type) *Chan { - return &Chan{dir: dir, elem: elem} -} - -// Dir returns the direction of channel c. -func (c *Chan) Dir() ChanDir { return c.dir } - -// Elem returns the element type of channel c. -func (c *Chan) Elem() Type { return c.elem } - -// A Named represents a named (defined) type. -type Named struct { - check *Checker // for Named.under implementation; nilled once under has been called - info typeInfo // for cycle detection - obj *TypeName // corresponding declared object - orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting) - underlying Type // possibly a *Named during setup; never a *Named once set up completely - tparams []*TypeName // type parameters, or nil - targs []Type // type arguments (after instantiation), or nil - methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily -} - -// NewNamed returns a new named type for the given type name, underlying type, and associated methods. -// If the given type name obj doesn't have a type yet, its type is set to the returned named type. -// The underlying type must not be a *Named. -func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { - if _, ok := underlying.(*Named); ok { - panic("types.NewNamed: underlying type must not be *Named") - } - return (*Checker)(nil).newNamed(obj, underlying, methods) + return nil } -func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named { - typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods} - if obj.typ == nil { - obj.typ = typ - } - // Ensure that typ is always expanded, at which point the check field can be - // nilled out. - // - // Note that currently we cannot nil out check inside typ.under(), because - // it's possible that typ is expanded multiple times. - // - // TODO(rFindley): clean this up so that under is the only function mutating - // named types. - if check != nil { - check.later(func() { - switch typ.under().(type) { - case *Named, *instance: - panic("internal error: unexpanded underlying type") +// If x and y are identical, match returns x. +// If x and y are identical channels but for their direction +// and one of them is unrestricted, match returns the channel +// with the restricted direction. +// In all other cases, match returns nil. +func match(x, y Type) Type { + // Common case: we don't have channels. + if Identical(x, y) { + return x + } + + // We may have channels that differ in direction only. + if x, _ := x.(*Chan); x != nil { + if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) { + // We have channels that differ in direction only. + // If there's an unrestricted channel, select the restricted one. + switch { + case x.dir == SendRecv: + return y + case y.dir == SendRecv: + return x } - typ.check = nil - }) - } - return typ -} - -// Obj returns the type name for the named type t. -func (t *Named) Obj() *TypeName { return t.obj } - -// TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. - -// _TParams returns the type parameters of the named type t, or nil. -// The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) _TParams() []*TypeName { return t.tparams } - -// _TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated. -func (t *Named) _TArgs() []Type { return t.targs } - -// _SetTArgs sets the type arguments of Named. -func (t *Named) _SetTArgs(args []Type) { t.targs = args } - -// NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.methods) } - -// Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). -func (t *Named) Method(i int) *Func { return t.methods[i] } - -// SetUnderlying sets the underlying type and marks t as complete. -func (t *Named) SetUnderlying(underlying Type) { - if underlying == nil { - panic("types.Named.SetUnderlying: underlying type must not be nil") - } - if _, ok := underlying.(*Named); ok { - panic("types.Named.SetUnderlying: underlying type must not be *Named") - } - t.underlying = underlying -} - -// AddMethod adds method m unless it is already in the method list. -func (t *Named) AddMethod(m *Func) { - if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { - t.methods = append(t.methods, m) - } -} - -// Note: This is a uint32 rather than a uint64 because the -// respective 64 bit atomic instructions are not available -// on all platforms. -var lastId uint32 - -// nextId returns a value increasing monotonically by 1 with -// each call, starting with 1. It may be called concurrently. -func nextId() uint64 { return uint64(atomic.AddUint32(&lastId, 1)) } - -// A _TypeParam represents a type parameter type. -type _TypeParam struct { - check *Checker // for lazy type bound completion - id uint64 // unique id - obj *TypeName // corresponding type name - index int // parameter index - bound Type // *Named or *Interface; underlying type is always *Interface -} - -// newTypeParam returns a new TypeParam. -func (check *Checker) newTypeParam(obj *TypeName, index int, bound Type) *_TypeParam { - assert(bound != nil) - typ := &_TypeParam{check: check, id: nextId(), obj: obj, index: index, bound: bound} - if obj.typ == nil { - obj.typ = typ - } - return typ -} - -func (t *_TypeParam) Bound() *Interface { - iface := asInterface(t.bound) - // use the type bound position if we have one - pos := token.NoPos - if n, _ := t.bound.(*Named); n != nil { - pos = n.obj.pos - } - // TODO(rFindley) switch this to an unexported method on Checker. - t.check.completeInterface(pos, iface) - return iface -} - -// optype returns a type's operational type. Except for -// type parameters, the operational type is the same -// as the underlying type (as returned by under). For -// Type parameters, the operational type is determined -// by the corresponding type bound's type list. The -// result may be the bottom or top type, but it is never -// the incoming type parameter. -func optype(typ Type) Type { - if t := asTypeParam(typ); t != nil { - // If the optype is typ, return the top type as we have - // no information. It also prevents infinite recursion - // via the asTypeParam converter function. This can happen - // for a type parameter list of the form: - // (type T interface { type T }). - // See also issue #39680. - if u := t.Bound().allTypes; u != nil && u != typ { - // u != typ and u is a type parameter => under(u) != typ, so this is ok - return under(u) - } - return theTop - } - return under(typ) -} - -// An instance represents an instantiated generic type syntactically -// (without expanding the instantiation). Type instances appear only -// during type-checking and are replaced by their fully instantiated -// (expanded) types before the end of type-checking. -type instance struct { - check *Checker // for lazy instantiation - pos token.Pos // position of type instantiation; for error reporting only - base *Named // parameterized type to be instantiated - targs []Type // type arguments - poslist []token.Pos // position of each targ; for error reporting only - value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set -} - -// expand returns the instantiated (= expanded) type of t. -// The result is either an instantiated *Named type, or -// Typ[Invalid] if there was an error. -func (t *instance) expand() Type { - v := t.value - if v == nil { - v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist) - if v == nil { - v = Typ[Invalid] } - t.value = v - } - // After instantiation we must have an invalid or a *Named type. - if debug && v != Typ[Invalid] { - _ = v.(*Named) - } - return v -} - -// expand expands a type instance into its instantiated -// type and leaves all other types alone. expand does -// not recurse. -func expand(typ Type) Type { - if t, _ := typ.(*instance); t != nil { - return t.expand() - } - return typ -} - -// expandf is set to expand. -// Call expandf when calling expand causes compile-time cycle error. -var expandf func(Type) Type - -func init() { expandf = expand } - -// bottom represents the bottom of the type lattice. -// It is the underlying type of a type parameter that -// cannot be satisfied by any type, usually because -// the intersection of type constraints left nothing). -type bottom struct{} - -// theBottom is the singleton bottom type. -var theBottom = &bottom{} - -// top represents the top of the type lattice. -// It is the underlying type of a type parameter that -// can be satisfied by any type (ignoring methods), -// usually because the type constraint has no type -// list. -type top struct{} - -// theTop is the singleton top type. -var theTop = &top{} - -// Type-specific implementations of Underlying. -func (t *Basic) Underlying() Type { return t } -func (t *Array) Underlying() Type { return t } -func (t *Slice) Underlying() Type { return t } -func (t *Struct) Underlying() Type { return t } -func (t *Pointer) Underlying() Type { return t } -func (t *Tuple) Underlying() Type { return t } -func (t *Signature) Underlying() Type { return t } -func (t *_Sum) Underlying() Type { return t } -func (t *Interface) Underlying() Type { return t } -func (t *Map) Underlying() Type { return t } -func (t *Chan) Underlying() Type { return t } -func (t *Named) Underlying() Type { return t.underlying } -func (t *_TypeParam) Underlying() Type { return t } -func (t *instance) Underlying() Type { return t } -func (t *bottom) Underlying() Type { return t } -func (t *top) Underlying() Type { return t } - -// Type-specific implementations of String. -func (t *Basic) String() string { return TypeString(t, nil) } -func (t *Array) String() string { return TypeString(t, nil) } -func (t *Slice) String() string { return TypeString(t, nil) } -func (t *Struct) String() string { return TypeString(t, nil) } -func (t *Pointer) String() string { return TypeString(t, nil) } -func (t *Tuple) String() string { return TypeString(t, nil) } -func (t *Signature) String() string { return TypeString(t, nil) } -func (t *_Sum) String() string { return TypeString(t, nil) } -func (t *Interface) String() string { return TypeString(t, nil) } -func (t *Map) String() string { return TypeString(t, nil) } -func (t *Chan) String() string { return TypeString(t, nil) } -func (t *Named) String() string { return TypeString(t, nil) } -func (t *_TypeParam) String() string { return TypeString(t, nil) } -func (t *instance) String() string { return TypeString(t, nil) } -func (t *bottom) String() string { return TypeString(t, nil) } -func (t *top) String() string { return TypeString(t, nil) } - -// under returns the true expanded underlying type. -// If it doesn't exist, the result is Typ[Invalid]. -// under must only be called when a type is known -// to be fully set up. -func under(t Type) Type { - // TODO(gri) is this correct for *Sum? - if n := asNamed(t); n != nil { - return n.under() } - return t -} - -// Converters -// -// A converter must only be called when a type is -// known to be fully set up. A converter returns -// a type's operational type (see comment for optype) -// or nil if the type argument is not of the -// respective type. - -func asBasic(t Type) *Basic { - op, _ := optype(t).(*Basic) - return op -} - -func asArray(t Type) *Array { - op, _ := optype(t).(*Array) - return op -} - -func asSlice(t Type) *Slice { - op, _ := optype(t).(*Slice) - return op -} - -func asStruct(t Type) *Struct { - op, _ := optype(t).(*Struct) - return op -} - -func asPointer(t Type) *Pointer { - op, _ := optype(t).(*Pointer) - return op -} -func asTuple(t Type) *Tuple { - op, _ := optype(t).(*Tuple) - return op -} - -func asSignature(t Type) *Signature { - op, _ := optype(t).(*Signature) - return op -} - -func asSum(t Type) *_Sum { - op, _ := optype(t).(*_Sum) - return op -} - -func asInterface(t Type) *Interface { - op, _ := optype(t).(*Interface) - return op -} - -func asMap(t Type) *Map { - op, _ := optype(t).(*Map) - return op -} - -func asChan(t Type) *Chan { - op, _ := optype(t).(*Chan) - return op -} - -// If the argument to asNamed and asTypeParam is of the respective types -// (possibly after expanding an instance type), these methods return that type. -// Otherwise the result is nil. - -func asNamed(t Type) *Named { - e, _ := expand(t).(*Named) - return e -} - -func asTypeParam(t Type) *_TypeParam { - u, _ := under(t).(*_TypeParam) - return u + // types are different + return nil } diff --git a/libgo/go/go/types/typelists.go b/libgo/go/go/types/typelists.go new file mode 100644 index 0000000..0f24135 --- /dev/null +++ b/libgo/go/go/types/typelists.go @@ -0,0 +1,69 @@ +// 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 types + +// TypeParamList holds a list of type parameters. +type TypeParamList struct{ tparams []*TypeParam } + +// Len returns the number of type parameters in the list. +// It is safe to call on a nil receiver. +func (l *TypeParamList) Len() int { return len(l.list()) } + +// At returns the i'th type parameter in the list. +func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] } + +// list is for internal use where we expect a []*TypeParam. +// TODO(rfindley): list should probably be eliminated: we can pass around a +// TypeParamList instead. +func (l *TypeParamList) list() []*TypeParam { + if l == nil { + return nil + } + return l.tparams +} + +// TypeList holds a list of types. +type TypeList struct{ types []Type } + +// newTypeList returns a new TypeList with the types in list. +func newTypeList(list []Type) *TypeList { + if len(list) == 0 { + return nil + } + return &TypeList{list} +} + +// Len returns the number of types in the list. +// It is safe to call on a nil receiver. +func (l *TypeList) Len() int { return len(l.list()) } + +// At returns the i'th type in the list. +func (l *TypeList) At(i int) Type { return l.types[i] } + +// list is for internal use where we expect a []Type. +// TODO(rfindley): list should probably be eliminated: we can pass around a +// TypeList instead. +func (l *TypeList) list() []Type { + if l == nil { + return nil + } + return l.types +} + +// ---------------------------------------------------------------------------- +// Implementation + +func bindTParams(list []*TypeParam) *TypeParamList { + if len(list) == 0 { + return nil + } + for i, typ := range list { + if typ.index >= 0 { + panic("type parameter bound more than once") + } + typ.index = i + } + return &TypeParamList{tparams: list} +} diff --git a/libgo/go/go/types/typeparam.go b/libgo/go/go/types/typeparam.go new file mode 100644 index 0000000..03ba9be --- /dev/null +++ b/libgo/go/go/types/typeparam.go @@ -0,0 +1,163 @@ +// Copyright 2011 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 types + +import ( + "go/token" + "sync/atomic" +) + +// Note: This is a uint32 rather than a uint64 because the +// respective 64 bit atomic instructions are not available +// on all platforms. +var lastID uint32 + +// nextID returns a value increasing monotonically by 1 with +// each call, starting with 1. It may be called concurrently. +func nextID() uint64 { return uint64(atomic.AddUint32(&lastID, 1)) } + +// A TypeParam represents a type parameter type. +type TypeParam struct { + check *Checker // for lazy type bound completion + id uint64 // unique id, for debugging only + obj *TypeName // corresponding type name + index int // type parameter index in source order, starting at 0 + bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface) +} + +// NewTypeParam returns a new TypeParam. Type parameters may be set on a Named +// or Signature type by calling SetTypeParams. Setting a type parameter on more +// than one type will result in a panic. +// +// The constraint argument can be nil, and set later via SetConstraint. +func NewTypeParam(obj *TypeName, constraint Type) *TypeParam { + return (*Checker)(nil).newTypeParam(obj, constraint) +} + +func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam { + // Always increment lastID, even if it is not used. + id := nextID() + if check != nil { + check.nextID++ + id = check.nextID + } + typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint} + if obj.typ == nil { + obj.typ = typ + } + // iface may mutate typ.bound, so we must ensure that iface() is called + // at least once before the resulting TypeParam escapes. + if check != nil { + check.later(func() { + typ.iface() + }) + } else if constraint != nil { + typ.iface() + } + return typ +} + +// Index returns the index of the type param within its param list, or -1 if +// the type parameter has not yet been bound to a type. +func (t *TypeParam) Index() int { + return t.index +} + +// Obj returns the type name for t. +func (t *TypeParam) Obj() *TypeName { return t.obj } + +// Constraint returns the type constraint specified for t. +func (t *TypeParam) Constraint() Type { + return t.bound +} + +// SetConstraint sets the type constraint for t. +// +// SetConstraint should not be called concurrently, but once SetConstraint +// returns the receiver t is safe for concurrent use. +func (t *TypeParam) SetConstraint(bound Type) { + if bound == nil { + panic("nil constraint") + } + t.bound = bound + // iface may mutate t.bound (if bound is not an interface), so ensure that + // this is done before returning. + t.iface() +} + +func (t *TypeParam) Underlying() Type { + return t.iface() +} + +func (t *TypeParam) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +// iface returns the constraint interface of t. +// TODO(gri) If we make tparamIsIface the default, this should be renamed to under +// (similar to Named.under). +func (t *TypeParam) iface() *Interface { + bound := t.bound + + // determine constraint interface + var ityp *Interface + switch u := under(bound).(type) { + case *Basic: + if u == Typ[Invalid] { + // error is reported elsewhere + return &emptyInterface + } + case *Interface: + if isTypeParam(bound) { + // error is reported in Checker.collectTypeParams + return &emptyInterface + } + ityp = u + } + + // If we don't have an interface, wrap constraint into an implicit interface. + if ityp == nil { + ityp = NewInterfaceType(nil, []Type{bound}) + ityp.implicit = true + t.bound = ityp // update t.bound for next time (optimization) + } + + // compute type set if necessary + if ityp.tset == nil { + // use the (original) type bound position if we have one + pos := token.NoPos + if n, _ := bound.(*Named); n != nil { + pos = n.obj.pos + } + computeInterfaceTypeSet(t.check, pos, ityp) + } + + return ityp +} + +// singleType returns the single type of the type parameter constraint; or nil. +func (t *TypeParam) singleType() Type { + return t.iface().typeSet().singleType() +} + +// hasTerms reports whether the type parameter constraint has specific type terms. +func (t *TypeParam) hasTerms() bool { + return t.iface().typeSet().hasTerms() +} + +// is calls f with the specific type terms of t's constraint and reports whether +// all calls to f returned true. If there are no specific terms, is +// returns the result of f(nil). +func (t *TypeParam) is(f func(*term) bool) bool { + return t.iface().typeSet().is(f) +} + +// underIs calls f with the underlying types of the specific type terms +// of t's constraint and reports whether all calls to f returned true. +// If there are no specific terms, underIs returns the result of f(nil). +func (t *TypeParam) underIs(f func(Type) bool) bool { + return t.iface().typeSet().underIs(f) +} diff --git a/libgo/go/go/types/types_test.go b/libgo/go/go/types/types_test.go deleted file mode 100644 index 25cd996..0000000 --- a/libgo/go/go/types/types_test.go +++ /dev/null @@ -1,19 +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 types - -import "sync/atomic" - -// Upon calling ResetId, nextId starts with 1 again. -// It may be called concurrently. This is only needed -// for tests where we may want to have a consistent -// numbering for each individual test case. -func ResetId() { atomic.StoreUint32(&lastId, 0) } - -// SetGoVersion sets the unexported goVersion field on config, so that tests -// which assert on behavior for older Go versions can set it. -func SetGoVersion(config *Config, goVersion string) { - config.goVersion = goVersion -} diff --git a/libgo/go/go/types/typeset.go b/libgo/go/go/types/typeset.go new file mode 100644 index 0000000..4598daa --- /dev/null +++ b/libgo/go/go/types/typeset.go @@ -0,0 +1,404 @@ +// 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 types + +import ( + "bytes" + "fmt" + "go/token" + "sort" +) + +// ---------------------------------------------------------------------------- +// API + +// A _TypeSet represents the type set of an interface. +type _TypeSet struct { + comparable bool // if set, the interface is or embeds comparable + // TODO(gri) consider using a set for the methods for faster lookup + methods []*Func // all methods of the interface; sorted by unique ID + terms termlist // type terms of the type set +} + +// IsEmpty reports whether type set s is the empty set. +func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() } + +// IsAll reports whether type set s is the set of all types (corresponding to the empty interface). +func (s *_TypeSet) IsAll() bool { return !s.comparable && len(s.methods) == 0 && s.terms.isAll() } + +// IsMethodSet reports whether the interface t is fully described by its method set. +func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() } + +// IsComparable reports whether each type in the set is comparable. +func (s *_TypeSet) IsComparable(seen map[Type]bool) bool { + if s.terms.isAll() { + return s.comparable + } + return s.is(func(t *term) bool { + return t != nil && comparable(t.typ, seen) + }) +} + +// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one. + +// IsTypeSet reports whether the type set s is represented by a finite set of underlying types. +func (s *_TypeSet) IsTypeSet() bool { + return !s.comparable && len(s.methods) == 0 +} + +// NumMethods returns the number of methods available. +func (s *_TypeSet) NumMethods() int { return len(s.methods) } + +// Method returns the i'th method of type set s for 0 <= i < s.NumMethods(). +// The methods are ordered by their unique ID. +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) String() string { + switch { + case s.IsEmpty(): + return "∅" + case s.IsAll(): + return "𝓤" + } + + hasMethods := len(s.methods) > 0 + hasTerms := s.hasTerms() + + var buf bytes.Buffer + buf.WriteByte('{') + if s.comparable { + buf.WriteString("comparable") + if hasMethods || hasTerms { + buf.WriteString("; ") + } + } + for i, m := range s.methods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.String()) + } + if hasMethods && hasTerms { + buf.WriteString("; ") + } + if hasTerms { + buf.WriteString(s.terms.String()) + } + buf.WriteString("}") + return buf.String() +} + +// ---------------------------------------------------------------------------- +// Implementation + +// hasTerms reports whether the type set has specific type terms. +func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() } + +// singleType returns the single type in s if there is exactly one; otherwise the result is nil. +func (s *_TypeSet) singleType() Type { return s.terms.singleType() } + +// subsetOf reports whether s1 ⊆ s2. +func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) } + +// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go + +// is calls f with the specific type terms of s and reports whether +// all calls to f returned true. If there are no specific terms, is +// returns the result of f(nil). +func (s *_TypeSet) is(f func(*term) bool) bool { + if !s.hasTerms() { + return f(nil) + } + for _, t := range s.terms { + assert(t.typ != nil) + if !f(t) { + return false + } + } + return true +} + +// underIs calls f with the underlying types of the specific type terms +// of s and reports whether all calls to f returned true. If there are +// no specific terms, underIs returns the result of f(nil). +func (s *_TypeSet) underIs(f func(Type) bool) bool { + if !s.hasTerms() { + return f(nil) + } + for _, t := range s.terms { + assert(t.typ != nil) + // x == under(x) for ~x terms + u := t.typ + if !t.tilde { + u = under(u) + } + if debug { + assert(Identical(u, under(u))) + } + if !f(u) { + return false + } + } + return true +} + +// topTypeSet may be used as type set for the empty interface. +var topTypeSet = _TypeSet{terms: allTermlist} + +// computeInterfaceTypeSet may be called with check == nil. +func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_TypeSet { + if ityp.tset != nil { + return ityp.tset + } + + // If the interface is not fully set up yet, the type set will + // not be complete, which may lead to errors when using the + // type set (e.g. missing method). Don't compute a partial type + // set (and don't store it!), so that we still compute the full + // type set eventually. Instead, return the top type set and + // let any follow-on errors play out. + // + // TODO(gri) Consider recording when this happens and reporting + // it as an error (but only if there were no other errors so to + // to not have unnecessary follow-on errors). + if !ityp.complete { + return &topTypeSet + } + + if check != nil && trace { + // Types don't generally have position information. + // If we don't have a valid pos provided, try to use + // one close enough. + if !pos.IsValid() && len(ityp.methods) > 0 { + pos = ityp.methods[0].pos + } + + check.trace(pos, "type set for %s", ityp) + check.indent++ + defer func() { + check.indent-- + check.trace(pos, "=> %s ", ityp.typeSet()) + }() + } + + // An infinitely expanding interface (due to a cycle) is detected + // elsewhere (Checker.validType), so here we simply assume we only + // have valid interfaces. Mark the interface as complete to avoid + // infinite recursion if the validType check occurs later for some + // reason. + ityp.tset = &_TypeSet{terms: allTermlist} // TODO(gri) is this sufficient? + + var unionSets map[*Union]*_TypeSet + if check != nil { + if check.unionTypeSets == nil { + check.unionTypeSets = make(map[*Union]*_TypeSet) + } + unionSets = check.unionTypeSets + } else { + unionSets = make(map[*Union]*_TypeSet) + } + + // Methods of embedded interfaces are collected unchanged; i.e., the identity + // of a method I.m's Func Object of an interface I is the same as that of + // the method m in an interface that embeds interface I. On the other hand, + // if a method is embedded via multiple overlapping embedded interfaces, we + // don't provide a guarantee which "original m" got chosen for the embedding + // interface. See also issue #34421. + // + // If we don't care to provide this identity guarantee anymore, instead of + // reusing the original method in embeddings, we can clone the method's Func + // Object and give it the position of a corresponding embedded interface. Then + // we can get rid of the mpos map below and simply use the cloned method's + // position. + + var todo []*Func + var seen objset + var methods []*Func + mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages + addMethod := func(pos token.Pos, m *Func, explicit bool) { + switch other := seen.insert(m); { + case other == nil: + methods = append(methods, m) + mpos[m] = pos + case explicit: + if check == nil { + panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name)) + } + // check != nil + check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) + check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented + default: + // We have a duplicate method name in an embedded (not explicitly declared) method. + // Check method signatures after all types are computed (issue #33656). + // If we're pre-go1.14 (overlapping embeddings are not permitted), report that + // error here as well (even though we could do it eagerly) because it's the same + // error message. + if check == nil { + // check method signatures after all locally embedded interfaces are computed + todo = append(todo, m, other.(*Func)) + break + } + // check != nil + check.later(func() { + if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) { + check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) + check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented + } + }) + } + } + + for _, m := range ityp.methods { + addMethod(m.pos, m, true) + } + + // collect embedded elements + var allTerms = allTermlist + for i, typ := range ityp.embeddeds { + // The embedding position is nil for imported interfaces + // and also for interface copies after substitution (but + // in that case we don't need to report errors again). + var pos token.Pos // embedding position + if ityp.embedPos != nil { + pos = (*ityp.embedPos)[i] + } + var terms termlist + switch u := under(typ).(type) { + case *Interface: + // For now we don't permit type parameters as constraints. + assert(!isTypeParam(typ)) + tset := computeInterfaceTypeSet(check, pos, u) + // If typ is local, an error was already reported where typ is specified/defined. + if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, 1, 18) { + check.errorf(atPos(pos), _UnsupportedFeature, "embedding constraint interface %s requires go1.18 or later", typ) + continue + } + if tset.comparable { + ityp.tset.comparable = true + } + for _, m := range tset.methods { + addMethod(pos, m, false) // use embedding position pos rather than m.pos + } + terms = tset.terms + case *Union: + if check != nil && !check.allowVersion(check.pkg, 1, 18) { + check.errorf(atPos(pos), _InvalidIfaceEmbed, "embedding interface element %s requires go1.18 or later", u) + continue + } + tset := computeUnionTypeSet(check, unionSets, pos, u) + if tset == &invalidTypeSet { + continue // ignore invalid unions + } + terms = tset.terms + default: + if u == Typ[Invalid] { + continue + } + if check != nil && !check.allowVersion(check.pkg, 1, 18) { + check.errorf(atPos(pos), _InvalidIfaceEmbed, "embedding non-interface type %s requires go1.18 or later", typ) + continue + } + terms = termlist{{false, typ}} + } + // The type set of an interface is the intersection + // of the type sets of all its elements. + // Intersection cannot produce longer termlists and + // thus cannot overflow. + allTerms = allTerms.intersect(terms) + } + ityp.embedPos = nil // not needed anymore (errors have been reported) + + // process todo's (this only happens if check == nil) + for i := 0; i < len(todo); i += 2 { + m := todo[i] + other := todo[i+1] + if !Identical(m.typ, other.typ) { + panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name)) + } + } + + if methods != nil { + sort.Sort(byUniqueMethodName(methods)) + ityp.tset.methods = methods + } + ityp.tset.terms = allTerms + + return ityp.tset +} + +func sortMethods(list []*Func) { + sort.Sort(byUniqueMethodName(list)) +} + +func assertSortedMethods(list []*Func) { + if !debug { + panic("assertSortedMethods called outside debug mode") + } + if !sort.IsSorted(byUniqueMethodName(list)) { + panic("methods not sorted") + } +} + +// byUniqueMethodName method lists can be sorted by their unique method names. +type byUniqueMethodName []*Func + +func (a byUniqueMethodName) Len() int { return len(a) } +func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() } +func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// invalidTypeSet is a singleton type set to signal an invalid type set +// due to an error. It's also a valid empty type set, so consumers of +// type sets may choose to ignore it. +var invalidTypeSet _TypeSet + +// computeUnionTypeSet may be called with check == nil. +// The result is &invalidTypeSet if the union overflows. +func computeUnionTypeSet(check *Checker, unionSets map[*Union]*_TypeSet, pos token.Pos, utyp *Union) *_TypeSet { + if tset, _ := unionSets[utyp]; tset != nil { + return tset + } + + // avoid infinite recursion (see also computeInterfaceTypeSet) + unionSets[utyp] = new(_TypeSet) + + var allTerms termlist + for _, t := range utyp.terms { + var terms termlist + u := under(t.typ) + if ui, _ := u.(*Interface); ui != nil { + // For now we don't permit type parameters as constraints. + assert(!isTypeParam(t.typ)) + terms = computeInterfaceTypeSet(check, pos, ui).terms + } else if t.typ == Typ[Invalid] { + continue + } else { + if t.tilde && !Identical(t.typ, u) { + // There is no underlying type which is t.typ. + // The corresponding type set is empty. + t = nil // ∅ term + } + terms = termlist{(*term)(t)} + } + // The type set of a union expression is the union + // of the type sets of each term. + allTerms = allTerms.union(terms) + if len(allTerms) > maxTermCount { + if check != nil { + check.errorf(atPos(pos), _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount) + } + unionSets[utyp] = &invalidTypeSet + return unionSets[utyp] + } + } + unionSets[utyp].terms = allTerms + + return unionSets[utyp] +} diff --git a/libgo/go/go/types/typeset_test.go b/libgo/go/go/types/typeset_test.go new file mode 100644 index 0000000..1c0eece --- /dev/null +++ b/libgo/go/go/types/typeset_test.go @@ -0,0 +1,81 @@ +// 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 types + +import ( + "go/ast" + "go/parser" + "go/token" + "testing" +) + +func TestInvalidTypeSet(t *testing.T) { + if !invalidTypeSet.IsEmpty() { + t.Error("invalidTypeSet is not empty") + } +} + +func TestTypeSetString(t *testing.T) { + for body, want := range map[string]string{ + "{}": "𝓤", + "{int}": "{int}", + "{~int}": "{~int}", + "{int|string}": "{int ∪ string}", + "{int; string}": "∅", + + "{comparable}": "{comparable}", + "{comparable; int}": "{comparable; int}", + "{~int; comparable}": "{comparable; ~int}", + "{int|string; comparable}": "{comparable; int ∪ string}", + "{comparable; int; string}": "∅", + + "{m()}": "{func (p.T).m()}", + "{m1(); m2() int }": "{func (p.T).m1(); func (p.T).m2() int}", + "{error}": "{func (error).Error() string}", + "{m(); comparable}": "{comparable; func (p.T).m()}", + "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}", + "{comparable; error}": "{comparable; func (error).Error() string}", + + "{m(); comparable; int|float32|string}": "{comparable; func (p.T).m(); int ∪ float32 ∪ string}", + "{m1(); int; m2(); comparable }": "{comparable; func (p.T).m1(); func (p.T).m2(); int}", + + "{E}; type E interface{}": "𝓤", + "{E}; type E interface{int;string}": "∅", + "{E}; type E interface{comparable}": "{comparable}", + } { + // parse + src := "package p; type T interface" + body + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "p.go", src, parser.AllErrors) + if file == nil { + t.Fatalf("%s: %v (invalid test case)", body, err) + } + + // type check + var conf Config + pkg, err := conf.Check(file.Name.Name, fset, []*ast.File{file}, nil) + if err != nil { + t.Fatalf("%s: %v (invalid test case)", body, err) + } + + // lookup T + obj := pkg.scope.Lookup("T") + if obj == nil { + t.Fatalf("%s: T not found (invalid test case)", body) + } + T, ok := under(obj.Type()).(*Interface) + if !ok { + t.Fatalf("%s: %v is not an interface (invalid test case)", body, obj) + } + + // verify test case + got := T.typeSet().String() + if got != want { + t.Errorf("%s: got %s; want %s", body, got, want) + } + } +} + +// TODO(gri) add more tests diff --git a/libgo/go/go/types/typestring.go b/libgo/go/go/types/typestring.go index fe27f0f..80210a2 100644 --- a/libgo/go/go/types/typestring.go +++ b/libgo/go/go/types/typestring.go @@ -10,6 +10,9 @@ import ( "bytes" "fmt" "go/token" + "sort" + "strconv" + "strings" "unicode/utf8" ) @@ -40,33 +43,18 @@ func RelativeTo(pkg *Package) Qualifier { } } -// If gcCompatibilityMode is set, printing of types is modified -// to match the representation of some types in the gc compiler: -// -// - byte and rune lose their alias name and simply stand for -// uint8 and int32 respectively -// - embedded interfaces get flattened (the embedding info is lost, -// and certain recursive interface types cannot be printed anymore) -// -// This makes it easier to compare packages computed with the type- -// checker vs packages imported from gc export data. -// -// Caution: This flag affects all uses of WriteType, globally. -// It is only provided for testing in conjunction with -// gc-generated data. -// -// This flag is exported in the x/tools/go/types package. We don't -// need it at the moment in the std repo and so we don't export it -// anymore. We should eventually try to remove it altogether. -// TODO(gri) remove this -var gcCompatibilityMode bool - // TypeString returns the string representation of typ. // The Qualifier controls the printing of // package-level objects, and may be nil. func TypeString(typ Type, qf Qualifier) string { + return typeString(typ, qf, false) +} + +func typeString(typ Type, qf Qualifier, debug bool) string { var buf bytes.Buffer - WriteType(&buf, typ, qf) + w := newTypeWriter(&buf, qf) + w.debug = debug + w.typ(typ) return buf.String() } @@ -74,172 +62,196 @@ func TypeString(typ Type, qf Qualifier) string { // The Qualifier controls the printing of // package-level objects, and may be nil. func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { - writeType(buf, typ, qf, make([]Type, 0, 8)) + newTypeWriter(buf, qf).typ(typ) +} + +// WriteSignature writes the representation of the signature sig to buf, +// without a leading "func" keyword. +// The Qualifier controls the printing of +// package-level objects, and may be nil. +func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { + newTypeWriter(buf, qf).signature(sig) +} + +type typeWriter struct { + buf *bytes.Buffer + seen map[Type]bool + qf Qualifier + ctxt *Context // if non-nil, we are type hashing + tparams *TypeParamList // local type parameters + debug bool // if true, write debug annotations +} + +func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { + return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false} +} + +func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter { + assert(ctxt != nil) + return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false} } -// instanceMarker is the prefix for an instantiated type -// in "non-evaluated" instance form. -const instanceMarker = '#' - -func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { - // Theoretically, this is a quadratic lookup algorithm, but in - // practice deeply nested composite types with unnamed component - // types are uncommon. This code is likely more efficient than - // using a map. - for _, t := range visited { - if t == typ { - fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ - return +func (w *typeWriter) byte(b byte) { + if w.ctxt != nil { + if b == ' ' { + b = '#' } + w.buf.WriteByte(b) + return + } + w.buf.WriteByte(b) + if b == ',' || b == ';' { + w.buf.WriteByte(' ') + } +} + +func (w *typeWriter) string(s string) { + w.buf.WriteString(s) +} + +func (w *typeWriter) error(msg string) { + if w.ctxt != nil { + panic(msg) } - visited = append(visited, typ) + w.buf.WriteString("<" + msg + ">") +} + +func (w *typeWriter) typ(typ Type) { + if w.seen[typ] { + w.error("cycle to " + goTypeName(typ)) + return + } + w.seen[typ] = true + defer delete(w.seen, typ) switch t := typ.(type) { case nil: - buf.WriteString("<nil>") + w.error("nil") case *Basic: // exported basic types go into package unsafe // (currently this is just unsafe.Pointer) if token.IsExported(t.name) { if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil { - writeTypeName(buf, obj, qf) + w.typeName(obj) break } } - - if gcCompatibilityMode { - // forget the alias names - switch t.kind { - case Byte: - t = Typ[Uint8] - case Rune: - t = Typ[Int32] - } - } - buf.WriteString(t.name) + w.string(t.name) case *Array: - fmt.Fprintf(buf, "[%d]", t.len) - writeType(buf, t.elem, qf, visited) + w.byte('[') + w.string(strconv.FormatInt(t.len, 10)) + w.byte(']') + w.typ(t.elem) case *Slice: - buf.WriteString("[]") - writeType(buf, t.elem, qf, visited) + w.string("[]") + w.typ(t.elem) case *Struct: - buf.WriteString("struct{") + w.string("struct{") for i, f := range t.fields { if i > 0 { - buf.WriteString("; ") + w.byte(';') } // This doesn't do the right thing for embedded type // aliases where we should print the alias name, not // the aliased type (see issue #44410). if !f.embedded { - buf.WriteString(f.name) - buf.WriteByte(' ') + w.string(f.name) + w.byte(' ') } - writeType(buf, f.typ, qf, visited) + w.typ(f.typ) if tag := t.Tag(i); tag != "" { - fmt.Fprintf(buf, " %q", tag) + w.byte(' ') + // TODO(rfindley) If tag contains blanks, replacing them with '#' + // in Context.TypeHash may produce another tag + // accidentally. + w.string(strconv.Quote(tag)) } } - buf.WriteByte('}') + w.byte('}') case *Pointer: - buf.WriteByte('*') - writeType(buf, t.base, qf, visited) + w.byte('*') + w.typ(t.base) case *Tuple: - writeTuple(buf, t, false, qf, visited) + w.tuple(t, false) case *Signature: - buf.WriteString("func") - writeSignature(buf, t, qf, visited) - - case *_Sum: - for i, t := range t.types { + w.string("func") + w.signature(t) + + case *Union: + // Unions only appear as (syntactic) embedded elements + // in interfaces and syntactically cannot be empty. + if t.Len() == 0 { + w.error("empty union") + break + } + for i, t := range t.terms { if i > 0 { - buf.WriteString(", ") + w.byte('|') } - writeType(buf, t, qf, visited) + if t.tilde { + w.byte('~') + } + w.typ(t.typ) } case *Interface: - // We write the source-level methods and embedded types rather - // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have embedded - // interface types that (directly or indirectly) embed the - // current interface. For instance, consider the result type - // of m: - // - // type T interface{ - // m() interface{ T } - // } - // - buf.WriteString("interface{") - empty := true - if gcCompatibilityMode { - // print flattened interface - // (useful to compare against gc-generated interfaces) - for i, m := range t.allMethods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - empty = false + if w.ctxt == nil { + if t == universeAny.Type() { + // When not hashing, we can try to improve type strings by writing "any" + // for a type that is pointer-identical to universeAny. This logic should + // be deprecated by more robust handling for aliases. + w.string("any") + break } - if !empty && t.allTypes != nil { - buf.WriteString("; ") + if t == universeComparable.Type().(*Named).underlying { + w.string("interface{comparable}") + break } - if t.allTypes != nil { - buf.WriteString("type ") - writeType(buf, t.allTypes, qf, visited) + } + if t.implicit { + if len(t.methods) == 0 && len(t.embeddeds) == 1 { + w.typ(t.embeddeds[0]) + break } + // Something's wrong with the implicit interface. + // Print it as such and continue. + w.string("/* implicit */ ") + } + w.string("interface{") + first := true + if w.ctxt != nil { + w.typeSet(t.typeSet()) } else { - // print explicit interface methods and embedded types - for i, m := range t.methods { - if i > 0 { - buf.WriteString("; ") + for _, m := range t.methods { + if !first { + w.byte(';') } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - empty = false + first = false + w.string(m.name) + w.signature(m.typ.(*Signature)) } - if !empty && t.types != nil { - buf.WriteString("; ") - } - if t.types != nil { - buf.WriteString("type ") - writeType(buf, t.types, qf, visited) - empty = false - } - if !empty && len(t.embeddeds) > 0 { - buf.WriteString("; ") - } - for i, typ := range t.embeddeds { - if i > 0 { - buf.WriteString("; ") + for _, typ := range t.embeddeds { + if !first { + w.byte(';') } - writeType(buf, typ, qf, visited) - empty = false - } - } - if t.allMethods == nil || len(t.methods) > len(t.allMethods) { - if !empty { - buf.WriteByte(' ') + first = false + w.typ(typ) } - buf.WriteString("/* incomplete */") } - buf.WriteByte('}') + w.byte('}') case *Map: - buf.WriteString("map[") - writeType(buf, t.key, qf, visited) - buf.WriteByte(']') - writeType(buf, t.elem, qf, visited) + w.string("map[") + w.typ(t.key) + w.byte(']') + w.typ(t.elem) case *Chan: var s string @@ -256,158 +268,187 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { case RecvOnly: s = "<-chan " default: - panic("unreachable") + w.error("unknown channel direction") } - buf.WriteString(s) + w.string(s) if parens { - buf.WriteByte('(') + w.byte('(') } - writeType(buf, t.elem, qf, visited) + w.typ(t.elem) if parens { - buf.WriteByte(')') + w.byte(')') } case *Named: - writeTypeName(buf, t.obj, qf) + // If hashing, write a unique prefix for t to represent its identity, since + // named type identity is pointer identity. + if w.ctxt != nil { + w.string(strconv.Itoa(w.ctxt.getID(t))) + } + w.typeName(t.obj) // when hashing written for readability of the hash only if t.targs != nil { // instantiated type - buf.WriteByte('[') - writeTypeList(buf, t.targs, qf, visited) - buf.WriteByte(']') - } else if t.tparams != nil { + w.typeList(t.targs.list()) + } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams // parameterized type - writeTParamList(buf, t.tparams, qf, visited) + w.tParamList(t.TypeParams().list()) } - case *_TypeParam: - s := "?" - if t.obj != nil { - s = t.obj.name + case *TypeParam: + if t.obj == nil { + w.error("unnamed type parameter") + break + } + if i := tparamIndex(w.tparams.list(), t); i >= 0 { + // The names of type parameters that are declared by the type being + // hashed are not part of the type identity. Replace them with a + // placeholder indicating their index. + w.string(fmt.Sprintf("$%d", i)) + } else { + w.string(t.obj.name) + if w.debug || w.ctxt != nil { + w.string(subscript(t.id)) + } } - buf.WriteString(s + subscript(t.id)) - - case *instance: - buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance - writeTypeName(buf, t.base.obj, qf) - buf.WriteByte('[') - writeTypeList(buf, t.targs, qf, visited) - buf.WriteByte(']') - - case *bottom: - buf.WriteString("⊥") - - case *top: - buf.WriteString("⊤") default: // For externally defined implementations of Type. - buf.WriteString(t.String()) + // Note: In this case cycles won't be caught. + w.string(t.String()) } } -func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) { +// typeSet writes a canonical hash for an interface type set. +func (w *typeWriter) typeSet(s *_TypeSet) { + assert(w.ctxt != nil) + first := true + for _, m := range s.methods { + if !first { + w.byte(';') + } + first = false + w.string(m.name) + w.signature(m.typ.(*Signature)) + } + switch { + case s.terms.isAll(): + // nothing to do + case s.terms.isEmpty(): + w.string(s.terms.String()) + default: + var termHashes []string + for _, term := range s.terms { + // terms are not canonically sorted, so we sort their hashes instead. + var buf bytes.Buffer + if term.tilde { + buf.WriteByte('~') + } + newTypeHasher(&buf, w.ctxt).typ(term.typ) + termHashes = append(termHashes, buf.String()) + } + sort.Strings(termHashes) + if !first { + w.byte(';') + } + w.string(strings.Join(termHashes, "|")) + } +} + +func (w *typeWriter) typeList(list []Type) { + w.byte('[') for i, typ := range list { if i > 0 { - buf.WriteString(", ") + w.byte(',') } - writeType(buf, typ, qf, visited) + w.typ(typ) } + w.byte(']') } -func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) { - // TODO(rFindley) compare this with the corresponding implementation in types2 - buf.WriteString("[") +func (w *typeWriter) tParamList(list []*TypeParam) { + w.byte('[') var prev Type - for i, p := range list { - // TODO(rFindley) support 'any' sugar here. - var b Type = &emptyInterface - if t, _ := p.typ.(*_TypeParam); t != nil && t.bound != nil { - b = t.bound + for i, tpar := range list { + // Determine the type parameter and its constraint. + // list is expected to hold type parameter names, + // but don't crash if that's not the case. + if tpar == nil { + w.error("nil type parameter") + continue } if i > 0 { - if b != prev { - // type bound changed - write previous one before advancing - buf.WriteByte(' ') - writeType(buf, prev, qf, visited) + if tpar.bound != prev { + // bound changed - write previous one before advancing + w.byte(' ') + w.typ(prev) } - buf.WriteString(", ") - } - prev = b - - if t, _ := p.typ.(*_TypeParam); t != nil { - writeType(buf, t, qf, visited) - } else { - buf.WriteString(p.name) + w.byte(',') } + prev = tpar.bound + w.typ(tpar) } if prev != nil { - buf.WriteByte(' ') - writeType(buf, prev, qf, visited) + w.byte(' ') + w.typ(prev) } - buf.WriteByte(']') + w.byte(']') } -func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) { - s := "<Named w/o object>" - if obj != nil { - if obj.pkg != nil { - writePackage(buf, obj.pkg, qf) - } - // TODO(gri): function-local named types should be displayed - // differently from named types at package level to avoid - // ambiguity. - s = obj.name +func (w *typeWriter) typeName(obj *TypeName) { + if obj.pkg != nil { + writePackage(w.buf, obj.pkg, w.qf) } - buf.WriteString(s) + w.string(obj.name) } -func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) { - buf.WriteByte('(') +func (w *typeWriter) tuple(tup *Tuple, variadic bool) { + w.byte('(') if tup != nil { for i, v := range tup.vars { if i > 0 { - buf.WriteString(", ") + w.byte(',') } - if v.name != "" { - buf.WriteString(v.name) - buf.WriteByte(' ') + // parameter names are ignored for type identity and thus type hashes + if w.ctxt == nil && v.name != "" { + w.string(v.name) + w.byte(' ') } typ := v.typ if variadic && i == len(tup.vars)-1 { if s, ok := typ.(*Slice); ok { - buf.WriteString("...") + w.string("...") typ = s.elem } else { // special case: // append(s, "foo"...) leads to signature func([]byte, string...) - if t := asBasic(typ); t == nil || t.kind != String { - panic("internal error: string type expected") + if t, _ := under(typ).(*Basic); t == nil || t.kind != String { + w.error("expected string type") + continue } - writeType(buf, typ, qf, visited) - buf.WriteString("...") + w.typ(typ) + w.string("...") continue } } - writeType(buf, typ, qf, visited) + w.typ(typ) } } - buf.WriteByte(')') + w.byte(')') } -// WriteSignature writes the representation of the signature sig to buf, -// without a leading "func" keyword. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { - writeSignature(buf, sig, qf, make([]Type, 0, 8)) -} - -func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { - if sig.tparams != nil { - writeTParamList(buf, sig.tparams, qf, visited) +func (w *typeWriter) signature(sig *Signature) { + if sig.TypeParams().Len() != 0 { + if w.ctxt != nil { + assert(w.tparams == nil) + w.tparams = sig.TypeParams() + defer func() { + w.tparams = nil + }() + } + w.tParamList(sig.TypeParams().list()) } - writeTuple(buf, sig.params, sig.variadic, qf, visited) + w.tuple(sig.params, sig.variadic) n := sig.results.Len() if n == 0 { @@ -415,15 +456,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T return } - buf.WriteByte(' ') - if n == 1 && sig.results.vars[0].name == "" { - // single unnamed result - writeType(buf, sig.results.vars[0].typ, qf, visited) + w.byte(' ') + if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { + // single unnamed result (if type hashing, name must be ignored) + w.typ(sig.results.vars[0].typ) return } // multiple or named result(s) - writeTuple(buf, sig.results, false, qf, visited) + w.tuple(sig.results, false) } // subscript returns the decimal (utf8) representation of x using subscript digits. diff --git a/libgo/go/go/types/typestring_test.go b/libgo/go/go/types/typestring_test.go index 07ed735..19e2a32 100644 --- a/libgo/go/go/types/typestring_test.go +++ b/libgo/go/go/types/typestring_test.go @@ -95,10 +95,13 @@ var independentTestTypes = []testEntry{ dup("interface{}"), dup("interface{m()}"), dup(`interface{String() string; m(int) float32}`), - - // TODO(rFindley) uncomment this once this AST is accepted, and add more test - // cases. - // dup(`interface{type int, float32, complex128}`), + dup("interface{int|float32|complex128}"), + dup("interface{int|~float32|~complex128}"), + dup("any"), + dup("interface{comparable}"), + // TODO(gri) adjust test for EvalCompositeTest + // {"comparable", "interface{comparable}"}, + // {"error", "interface{Error() string}"}, // maps dup("map[string]int"), @@ -135,65 +138,18 @@ func TestTypeString(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - typ := pkg.Scope().Lookup("T").Type().Underlying() + obj := pkg.Scope().Lookup("T") + if obj == nil { + t.Errorf("%s: T not found", test.src) + continue + } + typ := obj.Type().Underlying() if got := typ.String(); got != test.str { t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } } -func TestIncompleteInterfaces(t *testing.T) { - sig := NewSignature(nil, nil, nil, false) - m := NewFunc(token.NoPos, nil, "m", sig) - for _, test := range []struct { - typ *Interface - want string - }{ - {new(Interface), "interface{/* incomplete */}"}, - {new(Interface).Complete(), "interface{}"}, - - {NewInterface(nil, nil), "interface{}"}, - {NewInterface(nil, nil).Complete(), "interface{}"}, - {NewInterface([]*Func{}, nil), "interface{}"}, - {NewInterface([]*Func{}, nil).Complete(), "interface{}"}, - {NewInterface(nil, []*Named{}), "interface{}"}, - {NewInterface(nil, []*Named{}).Complete(), "interface{}"}, - {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"}, - {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"}, - {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"}, - - {NewInterfaceType(nil, nil), "interface{}"}, - {NewInterfaceType(nil, nil).Complete(), "interface{}"}, - {NewInterfaceType([]*Func{}, nil), "interface{}"}, - {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"}, - {NewInterfaceType(nil, []Type{}), "interface{}"}, - {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"}, - {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"}, - {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"}, - {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"}, - } { - got := test.typ.String() - if got != test.want { - t.Errorf("got: %s, want: %s", got, test.want) - } - } -} - -// newDefined creates a new defined type named T with the given underlying type. -// Helper function for use with TestIncompleteInterfaces only. -func newDefined(underlying Type) *Named { - tname := NewTypeName(token.NoPos, nil, "T", nil) - return NewNamed(tname, underlying, nil) -} - func TestQualifiedTypeString(t *testing.T) { t.Skip("skipping for gccgo--no importer") p, _ := pkgFor("p.go", "package p; type T int", nil) diff --git a/libgo/go/go/types/typeterm.go b/libgo/go/go/types/typeterm.go new file mode 100644 index 0000000..6b67821 --- /dev/null +++ b/libgo/go/go/types/typeterm.go @@ -0,0 +1,166 @@ +// 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 types + +// A term describes elementary type sets: +// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +// +type term struct { + tilde bool // valid if typ != nil + typ Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "∅" + case x.typ == nil: + return "𝓤" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // ∅ ⊂ x, y ⊂ 𝓤 + + return x.tilde == y.tilde && Identical(x.typ, y.typ) +} + +// union returns the union x ∪ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // ∅ ∪ ∅ == ∅ + case x == nil: + return y, nil // ∅ ∪ y == y + case y == nil: + return x, nil // x ∪ ∅ == x + case x.typ == nil: + return x, nil // 𝓤 ∪ y == 𝓤 + case y.typ == nil: + return y, nil // x ∪ 𝓤 == 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return x, y // x ∪ y == (x, y) if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∪ ~t == ~t + // ~t ∪ T == ~t + // T ∪ ~t == ~t + // T ∪ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ + case x.typ == nil: + return y // 𝓤 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓤 == x + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return nil // x ∩ y == ∅ if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ ∅ == false + case x.typ == nil: + return true // t ∈ 𝓤 == true + } + // ∅ ⊂ x ⊂ 𝓤 + + u := t + if x.tilde { + u = under(u) + } + return Identical(x.typ, u) +} + +// subsetOf reports whether x ⊆ y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // ∅ ⊆ y == true + case y == nil: + return false // x ⊆ ∅ == false since x != ∅ + case y.typ == nil: + return true // x ⊆ 𝓤 == true + case x.typ == nil: + return false // 𝓤 ⊆ y == false since y != 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return false // x ⊆ y == false if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ⊆ ~t == true + // ~t ⊆ T == false + // T ⊆ ~t == true + // T ⊆ T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == ∅. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !Identical(ux, uy) +} diff --git a/libgo/go/go/types/typeterm_test.go b/libgo/go/go/types/typeterm_test.go new file mode 100644 index 0000000..24a1410 --- /dev/null +++ b/libgo/go/go/types/typeterm_test.go @@ -0,0 +1,240 @@ +// 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 types + +import ( + "go/token" + "strings" + "testing" +) + +var myInt = func() Type { + tname := NewTypeName(token.NoPos, nil, "myInt", nil) + return NewNamed(tname, Typ[Int], nil) +}() + +var testTerms = map[string]*term{ + "∅": nil, + "𝓤": {}, + "int": {false, Typ[Int]}, + "~int": {true, Typ[Int]}, + "string": {false, Typ[String]}, + "~string": {true, Typ[String]}, + "myInt": {false, myInt}, +} + +func TestTermString(t *testing.T) { + for want, x := range testTerms { + if got := x.String(); got != want { + t.Errorf("%v.String() == %v; want %v", x, got, want) + } + } +} + +func split(s string, n int) []string { + r := strings.Split(s, " ") + if len(r) != n { + panic("invalid test case: " + s) + } + return r +} + +func testTerm(name string) *term { + r, ok := testTerms[name] + if !ok { + panic("invalid test argument: " + name) + } + return r +} + +func TestTermEqual(t *testing.T) { + for _, test := range []string{ + "∅ ∅ T", + "𝓤 𝓤 T", + "int int T", + "~int ~int T", + "myInt myInt T", + "∅ 𝓤 F", + "∅ int F", + "∅ ~int F", + "𝓤 int F", + "𝓤 ~int F", + "𝓤 myInt F", + "int ~int F", + "int myInt F", + "~int myInt F", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.equal(y); got != want { + t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want) + } + // equal is symmetric + x, y = y, x + if got := x.equal(y); got != want { + t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermUnion(t *testing.T) { + for _, test := range []string{ + "∅ ∅ ∅ ∅", + "∅ 𝓤 𝓤 ∅", + "∅ int int ∅", + "∅ ~int ~int ∅", + "∅ myInt myInt ∅", + "𝓤 𝓤 𝓤 ∅", + "𝓤 int 𝓤 ∅", + "𝓤 ~int 𝓤 ∅", + "𝓤 myInt 𝓤 ∅", + "int int int ∅", + "int ~int ~int ∅", + "int string int string", + "int ~string int ~string", + "int myInt int myInt", + "~int ~string ~int ~string", + "~int myInt ~int ∅", + + // union is symmetric, but the result order isn't - repeat symmetric cases explicitly + "𝓤 ∅ 𝓤 ∅", + "int ∅ int ∅", + "~int ∅ ~int ∅", + "myInt ∅ myInt ∅", + "int 𝓤 𝓤 ∅", + "~int 𝓤 𝓤 ∅", + "myInt 𝓤 𝓤 ∅", + "~int int ~int ∅", + "string int string int", + "~string int ~string int", + "myInt int myInt int", + "~string ~int ~string ~int", + "myInt ~int ~int ∅", + } { + args := split(test, 4) + x := testTerm(args[0]) + y := testTerm(args[1]) + want1 := testTerm(args[2]) + want2 := testTerm(args[3]) + if got1, got2 := x.union(y); !got1.equal(want1) || !got2.equal(want2) { + t.Errorf("%v.union(%v) = %v, %v; want %v, %v", x, y, got1, got2, want1, want2) + } + } +} + +func TestTermIntersection(t *testing.T) { + for _, test := range []string{ + "∅ ∅ ∅", + "∅ 𝓤 ∅", + "∅ int ∅", + "∅ ~int ∅", + "∅ myInt ∅", + "𝓤 𝓤 𝓤", + "𝓤 int int", + "𝓤 ~int ~int", + "𝓤 myInt myInt", + "int int int", + "int ~int int", + "int string ∅", + "int ~string ∅", + "int string ∅", + "~int ~string ∅", + "~int myInt myInt", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := testTerm(args[2]) + if got := x.intersect(y); !got.equal(want) { + t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want) + } + // intersect is symmetric + x, y = y, x + if got := x.intersect(y); !got.equal(want) { + t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermIncludes(t *testing.T) { + for _, test := range []string{ + "∅ int F", + "𝓤 int T", + "int int T", + "~int int T", + "~int myInt T", + "string int F", + "~string int F", + "myInt int F", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]).typ + want := args[2] == "T" + if got := x.includes(y); got != want { + t.Errorf("%v.includes(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermSubsetOf(t *testing.T) { + for _, test := range []string{ + "∅ ∅ T", + "𝓤 𝓤 T", + "int int T", + "~int ~int T", + "myInt myInt T", + "∅ 𝓤 T", + "∅ int T", + "∅ ~int T", + "∅ myInt T", + "𝓤 int F", + "𝓤 ~int F", + "𝓤 myInt F", + "int ~int T", + "int myInt F", + "~int myInt F", + "myInt int F", + "myInt ~int T", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.subsetOf(y); got != want { + t.Errorf("%v.subsetOf(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermDisjoint(t *testing.T) { + for _, test := range []string{ + "int int F", + "~int ~int F", + "int ~int F", + "int string T", + "int ~string T", + "int myInt T", + "~int ~string T", + "~int myInt F", + "string myInt T", + "~string myInt T", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.disjoint(y); got != want { + t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want) + } + // disjoint is symmetric + x, y = y, x + if got := x.disjoint(y); got != want { + t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want) + } + } +} diff --git a/libgo/go/go/types/typexpr.go b/libgo/go/go/types/typexpr.go index 1738d86..00c250b 100644 --- a/libgo/go/go/types/typexpr.go +++ b/libgo/go/go/types/typexpr.go @@ -11,9 +11,6 @@ import ( "go/ast" "go/constant" "go/internal/typeparams" - "go/token" - "sort" - "strconv" "strings" ) @@ -29,13 +26,27 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) // Note that we cannot use check.lookup here because the returned scope // may be different from obj.Parent(). See also Scope.LookupParent doc. scope, obj := check.scope.LookupParent(e.Name, check.pos) - if obj == nil { + switch obj { + case nil: if e.Name == "_" { - check.errorf(e, _InvalidBlank, "cannot use _ as value or type") + // Blank identifiers are never declared, but the current identifier may + // be a placeholder for a receiver type parameter. In this case we can + // resolve its type and object from Checker.recvTParamMap. + if tpar := check.recvTParamMap[e]; tpar != nil { + x.mode = typexpr + x.typ = tpar + } else { + check.error(e, _InvalidBlank, "cannot use _ as value or type") + } } else { check.errorf(e, _UndeclaredName, "undeclared name: %s", e.Name) } return + case universeAny, universeComparable: + if !check.allowVersion(check.pkg, 1, 18) { + check.errorf(e, _UndeclaredName, "undeclared name: %s (requires version go1.18 or later)", e.Name) + return // avoid follow-on errors + } } check.recordUse(e, obj) @@ -58,7 +69,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) // If so, mark the respective package as used. // (This code is only needed for dot-imports. Without them, // we only have to mark variables, see *Var case below). - if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil { + if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil { pkgName.used = true } @@ -85,6 +96,10 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) x.mode = constant_ case *TypeName: + if check.isBrokenAlias(obj) { + check.errorf(e, _InvalidDeclCycle, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name) + return + } x.mode = typexpr case *Var: @@ -125,41 +140,32 @@ func (check *Checker) typ(e ast.Expr) Type { } // varType type-checks the type expression e and returns its type, or Typ[Invalid]. -// The type must not be an (uninstantiated) generic type and it must be ordinary -// (see ordinaryType). +// The type must not be an (uninstantiated) generic type and it must not be a +// constraint interface. func (check *Checker) varType(e ast.Expr) Type { typ := check.definedType(e, nil) - check.ordinaryType(e, typ) - return typ -} -// ordinaryType reports an error if typ is an interface type containing -// type lists or is (or embeds) the predeclared type comparable. -func (check *Checker) ordinaryType(pos positioner, typ Type) { - // We don't want to call under() (via asInterface) or complete interfaces - // while we are in the middle of type-checking parameter declarations that - // might belong to interface methods. Delay this check to the end of - // type-checking. + // If we have a type parameter there's nothing to do. + if isTypeParam(typ) { + return typ + } + + // We don't want to call under() or complete interfaces while we are in + // the middle of type-checking parameter declarations that might belong + // to interface methods. Delay this check to the end of type-checking. check.later(func() { - if t := asInterface(typ); t != nil { - check.completeInterface(pos.Pos(), t) // TODO(gri) is this the correct position? - if t.allTypes != nil { - check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes) - return - } - if t._IsComparable() { - check.softErrorf(pos, _Todo, "interface is (or embeds) comparable") + if t, _ := under(typ).(*Interface); t != nil { + tset := computeInterfaceTypeSet(check, e.Pos(), t) // TODO(gri) is this the correct position? + if !tset.IsMethodSet() { + if tset.comparable { + check.softErrorf(e, _MisplacedConstraintIface, "interface is (or embeds) comparable") + } else { + check.softErrorf(e, _MisplacedConstraintIface, "interface contains type constraints") + } } } }) -} -// anyType type-checks the type expression e and returns its type, or Typ[Invalid]. -// The type may be generic or instantiated. -func (check *Checker) anyType(e ast.Expr) Type { - typ := check.typInternal(e, nil) - assert(isTyped(typ)) - check.recordTypeAndValue(e, typexpr, typ, nil) return typ } @@ -172,20 +178,22 @@ func (check *Checker) definedType(e ast.Expr, def *Named) Type { typ := check.typInternal(e, def) assert(isTyped(typ)) if isGeneric(typ) { - check.errorf(e, _Todo, "cannot use generic type %s without instantiation", typ) + check.errorf(e, _WrongTypeArgCount, "cannot use generic type %s without instantiation", typ) typ = Typ[Invalid] } check.recordTypeAndValue(e, typexpr, typ, nil) return typ } -// genericType is like typ but the type must be an (uninstantiated) generic type. -func (check *Checker) genericType(e ast.Expr, reportErr bool) Type { +// genericType is like typ but the type must be an (uninstantiated) generic +// type. If reason is non-nil and the type expression was a valid type but not +// generic, reason will be populated with a message describing the error. +func (check *Checker) genericType(e ast.Expr, reason *string) Type { typ := check.typInternal(e, nil) assert(isTyped(typ)) if typ != Typ[Invalid] && !isGeneric(typ) { - if reportErr { - check.errorf(e, _Todo, "%s is not a generic type", typ) + if reason != nil { + *reason = check.sprintf("%s is not a generic type", typ) } typ = Typ[Invalid] } @@ -194,205 +202,6 @@ func (check *Checker) genericType(e ast.Expr, reportErr bool) Type { return typ } -// isubst returns an x with identifiers substituted per the substitution map smap. -// isubst only handles the case of (valid) method receiver type expressions correctly. -func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr { - switch n := x.(type) { - case *ast.Ident: - if alt := smap[n]; alt != nil { - return alt - } - case *ast.StarExpr: - X := isubst(n.X, smap) - if X != n.X { - new := *n - new.X = X - return &new - } - case *ast.IndexExpr: - elems := typeparams.UnpackExpr(n.Index) - var newElems []ast.Expr - for i, elem := range elems { - new := isubst(elem, smap) - if new != elem { - if newElems == nil { - newElems = make([]ast.Expr, len(elems)) - copy(newElems, elems) - } - newElems[i] = new - } - } - if newElems != nil { - index := typeparams.PackExpr(newElems) - new := *n - new.Index = index - return &new - } - case *ast.ParenExpr: - return isubst(n.X, smap) // no need to keep parentheses - default: - // Other receiver type expressions are invalid. - // It's fine to ignore those here as they will - // be checked elsewhere. - } - return x -} - -// funcType type-checks a function or method type. -func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) { - check.openScope(ftyp, "function") - check.scope.isFunc = true - check.recordScope(ftyp, check.scope) - sig.scope = check.scope - defer check.closeScope() - - var recvTyp ast.Expr // rewritten receiver type; valid if != nil - if recvPar != nil && len(recvPar.List) > 0 { - // collect generic receiver type parameters, if any - // - a receiver type parameter is like any other type parameter, except that it is declared implicitly - // - the receiver specification acts as local declaration for its type parameters, which may be blank - _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true) - if len(rparams) > 0 { - // Blank identifiers don't get declared and regular type-checking of the instantiated - // parameterized receiver type expression fails in Checker.collectParams of receiver. - // Identify blank type parameters and substitute each with a unique new identifier named - // "n_" (where n is the parameter index) and which cannot conflict with any user-defined - // name. - var smap map[*ast.Ident]*ast.Ident // substitution map from "_" to "n_" identifiers - for i, p := range rparams { - if p.Name == "_" { - new := *p - new.Name = fmt.Sprintf("%d_", i) - rparams[i] = &new // use n_ identifier instead of _ so it can be looked up - if smap == nil { - smap = make(map[*ast.Ident]*ast.Ident) - } - smap[p] = &new - } - } - if smap != nil { - // blank identifiers were found => use rewritten receiver type - recvTyp = isubst(recvPar.List[0].Type, smap) - } - sig.rparams = check.declareTypeParams(nil, rparams) - // determine receiver type to get its type parameters - // and the respective type parameter bounds - var recvTParams []*TypeName - if rname != nil { - // recv should be a Named type (otherwise an error is reported elsewhere) - // Also: Don't report an error via genericType since it will be reported - // again when we type-check the signature. - // TODO(gri) maybe the receiver should be marked as invalid instead? - if recv := asNamed(check.genericType(rname, false)); recv != nil { - recvTParams = recv.tparams - } - } - // provide type parameter bounds - // - only do this if we have the right number (otherwise an error is reported elsewhere) - if len(sig.rparams) == len(recvTParams) { - // We have a list of *TypeNames but we need a list of Types. - list := make([]Type, len(sig.rparams)) - for i, t := range sig.rparams { - list[i] = t.typ - } - smap := makeSubstMap(recvTParams, list) - for i, tname := range sig.rparams { - bound := recvTParams[i].typ.(*_TypeParam).bound - // bound is (possibly) parameterized in the context of the - // receiver type declaration. Substitute parameters for the - // current context. - // TODO(gri) should we assume now that bounds always exist? - // (no bound == empty interface) - if bound != nil { - bound = check.subst(tname.pos, bound, smap) - tname.typ.(*_TypeParam).bound = bound - } - } - } - } - } - - if tparams := typeparams.Get(ftyp); tparams != nil { - sig.tparams = check.collectTypeParams(tparams) - // Always type-check method type parameters but complain that they are not allowed. - // (A separate check is needed when type-checking interface method signatures because - // they don't have a receiver specification.) - if recvPar != nil { - check.errorf(tparams, _Todo, "methods cannot have type parameters") - } - } - - // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their - // declarations and then squash that scope into the parent scope (and report any redeclarations at - // that time). - scope := NewScope(check.scope, token.NoPos, token.NoPos, "function body (temp. scope)") - recvList, _ := check.collectParams(scope, recvPar, recvTyp, false) // use rewritten receiver type, if any - params, variadic := check.collectParams(scope, ftyp.Params, nil, true) - results, _ := check.collectParams(scope, ftyp.Results, nil, false) - scope.squash(func(obj, alt Object) { - check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name()) - check.reportAltDecl(alt) - }) - - if recvPar != nil { - // recv parameter list present (may be empty) - // spec: "The receiver is specified via an extra parameter section preceding the - // method name. That parameter section must declare a single parameter, the receiver." - var recv *Var - switch len(recvList) { - case 0: - // error reported by resolver - recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below - default: - // more than one receiver - check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver") - fallthrough // continue with first receiver - case 1: - recv = recvList[0] - } - - // TODO(gri) We should delay rtyp expansion to when we actually need the - // receiver; thus all checks here should be delayed to later. - rtyp, _ := deref(recv.typ) - rtyp = expand(rtyp) - - // spec: "The receiver type must be of the form T or *T where T is a type name." - // (ignore invalid types - error was reported before) - if t := rtyp; t != Typ[Invalid] { - var err string - if T := asNamed(t); T != nil { - // spec: "The type denoted by T is called the receiver base type; it must not - // be a pointer or interface type and it must be declared in the same package - // as the method." - if T.obj.pkg != check.pkg { - err = "type not defined in this package" - } else { - switch u := optype(T).(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" - } - case *Pointer, *Interface: - err = "pointer or interface type" - } - } - } else { - err = "basic or unnamed type" - } - if err != "" { - check.errorf(recv, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err) - // ok to continue - } - } - sig.recv = recv - } - - sig.params = NewTuple(params...) - sig.results = NewTuple(results...) - sig.variadic = variadic -} - // goTypeName returns the Go type name for typ and // removes any occurrences of "types." from that name. func goTypeName(typ Type) string { @@ -404,7 +213,7 @@ func goTypeName(typ Type) string { // func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { if trace { - check.trace(e0.Pos(), "type %s", e0) + check.trace(e0.Pos(), "-- type %s", e0) check.indent++ defer func() { check.indent-- @@ -412,9 +221,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { if T != nil { // Calling under() here may lead to endless instantiations. // Test case: type T[P any] *T[P] - // TODO(gri) investigate if that's a bug or to be expected - // (see also analogous comment in Checker.instantiate). - under = T.Underlying() + under = safeUnderlying(T) } if T == under { check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T)) @@ -462,13 +269,12 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { check.errorf(&x, _NotAType, "%s is not a type", &x) } - case *ast.IndexExpr: - if typeparams.Enabled { - exprs := typeparams.UnpackExpr(e.Index) - return check.instantiatedType(e.X, exprs, def) + case *ast.IndexExpr, *ast.IndexListExpr: + ix := typeparams.UnpackIndexExpr(e) + if !check.allowVersion(check.pkg, 1, 18) { + check.softErrorf(inNode(e, ix.Lbrack), _UnsupportedFeature, "type instantiation requires go1.18 or later") } - check.errorf(e0, _NotAType, "%s is not a type", e0) - check.use(e.X) + return check.instantiatedType(ix, def) case *ast.ParenExpr: // Generic types must be instantiated before they can be used in any form. @@ -476,18 +282,20 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { return check.definedType(e.X, def) case *ast.ArrayType: - if e.Len != nil { - typ := new(Array) + if e.Len == nil { + typ := new(Slice) def.setUnderlying(typ) - typ.len = check.arrayLength(e.Len) typ.elem = check.varType(e.Elt) return typ } - typ := new(Slice) + typ := new(Array) def.setUnderlying(typ) + typ.len = check.arrayLength(e.Len) typ.elem = check.varType(e.Elt) - return typ + if typ.len >= 0 { + return typ + } case *ast.Ellipsis: // dots are handled explicitly where they are legal @@ -503,6 +311,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { case *ast.StarExpr: typ := new(Pointer) + typ.base = Typ[Invalid] // avoid nil base in invalid recursive type declaration def.setUnderlying(typ) typ.base = check.varType(e.X) return typ @@ -538,7 +347,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { check.later(func() { if !Comparable(typ.key) { var why string - if asTypeParam(typ.key) != nil { + if isTypeParam(typ.key) { why = " (missing comparable constraint)" } check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why) @@ -577,80 +386,115 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { return typ } -// typeOrNil type-checks the type expression (or nil value) e -// and returns the type of e, or nil. If e is a type, it must -// not be an (uninstantiated) generic type. -// If e is neither a type nor nil, typeOrNil returns Typ[Invalid]. -// TODO(gri) should we also disallow non-var types? -func (check *Checker) typeOrNil(e ast.Expr) Type { - var x operand - check.rawExpr(&x, e, nil) - switch x.mode { - case invalid: - // ignore - error reported before - case novalue: - check.errorf(&x, _NotAType, "%s used as type", &x) - case typexpr: - check.instantiatedOperand(&x) - return x.typ - case value: - if x.isNil() { - return nil - } - fallthrough - default: - check.errorf(&x, _NotAType, "%s is not a type", &x) +func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (res Type) { + pos := ix.X.Pos() + if trace { + check.trace(pos, "-- instantiating %s with %s", ix.X, ix.Indices) + check.indent++ + defer func() { + check.indent-- + // Don't format the underlying here. It will always be nil. + check.trace(pos, "=> %s", res) + }() } - return Typ[Invalid] -} -func (check *Checker) instantiatedType(x ast.Expr, targs []ast.Expr, def *Named) Type { - b := check.genericType(x, true) // TODO(gri) what about cycles? - if b == Typ[Invalid] { - return b // error already reported + var reason string + gtyp := check.genericType(ix.X, &reason) + if reason != "" { + check.invalidOp(ix.Orig, _NotAGenericType, "%s (%s)", ix.Orig, reason) } - base := asNamed(b) - if base == nil { - unreachable() // should have been caught by genericType + if gtyp == Typ[Invalid] { + return gtyp // error already reported } - // create a new type instance rather than instantiate the type - // TODO(gri) should do argument number check here rather than - // when instantiating the type? - typ := new(instance) - def.setUnderlying(typ) - - typ.check = check - typ.pos = x.Pos() - typ.base = base + orig, _ := gtyp.(*Named) + if orig == nil { + panic(fmt.Sprintf("%v: cannot instantiate %v", ix.Pos(), gtyp)) + } - // evaluate arguments (always) - typ.targs = check.typeList(targs) - if typ.targs == nil { + // evaluate arguments + targs := check.typeList(ix.Indices) + if targs == nil { def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation return Typ[Invalid] } - // determine argument positions (for error reporting) - typ.poslist = make([]token.Pos, len(targs)) - for i, arg := range targs { - typ.poslist[i] = arg.Pos() + // create the instance + ctxt := check.bestContext(nil) + h := ctxt.instanceHash(orig, targs) + // targs may be incomplete, and require inference. In any case we should de-duplicate. + inst, _ := ctxt.lookup(h, orig, targs).(*Named) + // If inst is non-nil, we can't just return here. Inst may have been + // constructed via recursive substitution, in which case we wouldn't do the + // validation below. Ensure that the validation (and resulting errors) runs + // for each instantiated type in the source. + if inst == nil { + // x may be a selector for an imported type; use its start pos rather than x.Pos(). + tname := NewTypeName(ix.Pos(), orig.obj.pkg, orig.obj.name, nil) + inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved + inst.targs = newTypeList(targs) + inst = ctxt.update(h, orig, targs, inst).(*Named) + } + def.setUnderlying(inst) + + inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) { + tparams := orig.TypeParams().list() + + inferred := targs + if len(targs) < len(tparams) { + // If inference fails, len(inferred) will be 0, and inst.underlying will + // be set to Typ[Invalid] in expandNamed. + inferred = check.infer(ix.Orig, tparams, targs, nil, nil) + if len(inferred) > len(targs) { + inst.targs = newTypeList(inferred) + } + } + + check.recordInstance(ix.Orig, inferred, inst) + return expandNamed(ctxt, n, pos) } - // make sure we check instantiation works at least once - // and that the resulting type is valid + // orig.tparams may not be set up, so we need to do expansion later. check.later(func() { - t := typ.expand() - check.validType(t, nil) + // This is an instance from the source, not from recursive substitution, + // and so it must be resolved during type-checking so that we can report + // errors. + inst.resolve(ctxt) + // Since check is non-nil, we can still mutate inst. Unpinning the resolver + // frees some memory. + inst.resolver = nil + + if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) { + if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil { + // best position for error reporting + pos := ix.Pos() + if i < len(ix.Indices) { + pos = ix.Indices[i].Pos() + } + check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error()) + } else { + check.mono.recordInstance(check.pkg, pos, inst.tparams.list(), inst.targs.list(), ix.Indices) + } + } + + check.validType(inst) }) - return typ + return inst } // arrayLength type-checks the array length expression e // and returns the constant length >= 0, or a value < 0 // to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e ast.Expr) int64 { + // If e is an undeclared identifier, the array declaration might be an + // attempt at a parameterized type declaration with missing constraint. + // Provide a better error message than just "undeclared name: X". + if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil { + check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name) + return -1 + } + var x operand check.expr(&x, e) if x.mode != constant_ { @@ -659,6 +503,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { } return -1 } + if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check, Typ[Int], nil) { @@ -670,6 +515,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { } } } + check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x) return -1 } @@ -689,518 +535,3 @@ func (check *Checker) typeList(list []ast.Expr) []Type { } return res } - -// collectParams declares the parameters of list in scope and returns the corresponding -// variable list. If type0 != nil, it is used instead of the first type in list. -func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, type0 ast.Expr, variadicOk bool) (params []*Var, variadic bool) { - if list == nil { - return - } - - var named, anonymous bool - for i, field := range list.List { - ftype := field.Type - if i == 0 && type0 != nil { - ftype = type0 - } - if t, _ := ftype.(*ast.Ellipsis); t != nil { - ftype = t.Elt - if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 { - variadic = true - } else { - check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list") - // ignore ... and continue - } - } - typ := check.varType(ftype) - // The parser ensures that f.Tag is nil and we don't - // care if a constructed AST contains a non-nil tag. - if len(field.Names) > 0 { - // named parameter - for _, name := range field.Names { - if name.Name == "" { - check.invalidAST(name, "anonymous parameter") - // ok to continue - } - par := NewParam(name.Pos(), check.pkg, name.Name, typ) - check.declare(scope, name, par, scope.pos) - params = append(params, par) - } - named = true - } else { - // anonymous parameter - par := NewParam(ftype.Pos(), check.pkg, "", typ) - check.recordImplicit(field, par) - params = append(params, par) - anonymous = true - } - } - - if named && anonymous { - check.invalidAST(list, "list contains both named and anonymous parameters") - // ok to continue - } - - // For a variadic function, change the last parameter's type from T to []T. - // Since we type-checked T rather than ...T, we also need to retro-actively - // record the type for ...T. - if variadic { - last := params[len(params)-1] - last.typ = &Slice{elem: last.typ} - check.recordTypeAndValue(list.List[len(list.List)-1].Type, typexpr, last.typ, nil) - } - - return -} - -func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool { - if alt := oset.insert(obj); alt != nil { - check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name()) - check.reportAltDecl(alt) - return false - } - return true -} - -func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) { - var tlist *ast.Ident // "type" name of first entry in a type list declaration - var types []ast.Expr - for _, f := range iface.Methods.List { - if len(f.Names) > 0 { - // We have a method with name f.Names[0], or a type - // of a type list (name.Name == "type"). - // (The parser ensures that there's only one method - // and we don't care if a constructed AST has more.) - name := f.Names[0] - if name.Name == "_" { - check.errorf(name, _BlankIfaceMethod, "invalid method name _") - continue // ignore - } - - if name.Name == "type" { - // Always collect all type list entries, even from - // different type lists, under the assumption that - // the author intended to include all types. - types = append(types, f.Type) - if tlist != nil && tlist != name { - check.errorf(name, _Todo, "cannot have multiple type lists in an interface") - } - tlist = name - continue - } - - typ := check.typ(f.Type) - sig, _ := typ.(*Signature) - if sig == nil { - if typ != Typ[Invalid] { - check.invalidAST(f.Type, "%s is not a method signature", typ) - } - continue // ignore - } - - // Always type-check method type parameters but complain if they are not enabled. - // (This extra check is needed here because interface method signatures don't have - // a receiver specification.) - if sig.tparams != nil { - var at positioner = f.Type - if tparams := typeparams.Get(f.Type); tparams != nil { - at = tparams - } - check.errorf(at, _Todo, "methods cannot have type parameters") - } - - // use named receiver type if available (for better error messages) - var recvTyp Type = ityp - if def != nil { - recvTyp = def - } - sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp) - - m := NewFunc(name.Pos(), check.pkg, name.Name, sig) - check.recordDef(name, m) - ityp.methods = append(ityp.methods, m) - } else { - // We have an embedded type. completeInterface will - // eventually verify that we have an interface. - ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type)) - check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos()) - } - } - - // type constraints - ityp.types = _NewSum(check.collectTypeConstraints(iface.Pos(), types)) - - if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 { - // empty interface - ityp.allMethods = markComplete - return - } - - // sort for API stability - sortMethods(ityp.methods) - sortTypes(ityp.embeddeds) - - check.later(func() { check.completeInterface(iface.Pos(), ityp) }) -} - -func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) { - if ityp.allMethods != nil { - return - } - - // completeInterface may be called via the LookupFieldOrMethod, - // MissingMethod, Identical, or IdenticalIgnoreTags external API - // in which case check will be nil. In this case, type-checking - // must be finished and all interfaces should have been completed. - if check == nil { - panic("internal error: incomplete interface") - } - - if trace { - // Types don't generally have position information. - // If we don't have a valid pos provided, try to use - // one close enough. - if !pos.IsValid() && len(ityp.methods) > 0 { - pos = ityp.methods[0].pos - } - - check.trace(pos, "complete %s", ityp) - check.indent++ - defer func() { - check.indent-- - check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes) - }() - } - - // An infinitely expanding interface (due to a cycle) is detected - // elsewhere (Checker.validType), so here we simply assume we only - // have valid interfaces. Mark the interface as complete to avoid - // infinite recursion if the validType check occurs later for some - // reason. - ityp.allMethods = markComplete - - // Methods of embedded interfaces are collected unchanged; i.e., the identity - // of a method I.m's Func Object of an interface I is the same as that of - // the method m in an interface that embeds interface I. On the other hand, - // if a method is embedded via multiple overlapping embedded interfaces, we - // don't provide a guarantee which "original m" got chosen for the embedding - // interface. See also issue #34421. - // - // If we don't care to provide this identity guarantee anymore, instead of - // reusing the original method in embeddings, we can clone the method's Func - // Object and give it the position of a corresponding embedded interface. Then - // we can get rid of the mpos map below and simply use the cloned method's - // position. - - var seen objset - var methods []*Func - mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages - addMethod := func(pos token.Pos, m *Func, explicit bool) { - switch other := seen.insert(m); { - case other == nil: - methods = append(methods, m) - mpos[m] = pos - case explicit: - check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) - check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented - default: - // We have a duplicate method name in an embedded (not explicitly declared) method. - // Check method signatures after all types are computed (issue #33656). - // If we're pre-go1.14 (overlapping embeddings are not permitted), report that - // error here as well (even though we could do it eagerly) because it's the same - // error message. - check.later(func() { - if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) { - check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) - check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented - } - }) - } - } - - for _, m := range ityp.methods { - addMethod(m.pos, m, true) - } - - // collect types - allTypes := ityp.types - - posList := check.posMap[ityp] - for i, typ := range ityp.embeddeds { - pos := posList[i] // embedding position - utyp := under(typ) - etyp := asInterface(utyp) - if etyp == nil { - if utyp != Typ[Invalid] { - var format string - if _, ok := utyp.(*_TypeParam); ok { - format = "%s is a type parameter, not an interface" - } else { - format = "%s is not an interface" - } - // TODO: correct error code. - check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ) - } - continue - } - check.completeInterface(pos, etyp) - for _, m := range etyp.allMethods { - addMethod(pos, m, false) // use embedding position pos rather than m.pos - } - allTypes = intersect(allTypes, etyp.allTypes) - } - - if methods != nil { - sort.Sort(byUniqueMethodName(methods)) - ityp.allMethods = methods - } - ityp.allTypes = allTypes -} - -// intersect computes the intersection of the types x and y. -// Note: A incomming nil type stands for the top type. A top -// type result is returned as nil. -func intersect(x, y Type) (r Type) { - defer func() { - if r == theTop { - r = nil - } - }() - - switch { - case x == theBottom || y == theBottom: - return theBottom - case x == nil || x == theTop: - return y - case y == nil || x == theTop: - return x - } - - xtypes := unpackType(x) - ytypes := unpackType(y) - // Compute the list rtypes which includes only - // types that are in both xtypes and ytypes. - // Quadratic algorithm, but good enough for now. - // TODO(gri) fix this - var rtypes []Type - for _, x := range xtypes { - if includes(ytypes, x) { - rtypes = append(rtypes, x) - } - } - - if rtypes == nil { - return theBottom - } - return _NewSum(rtypes) -} - -func sortTypes(list []Type) { - sort.Stable(byUniqueTypeName(list)) -} - -// byUniqueTypeName named type lists can be sorted by their unique type names. -type byUniqueTypeName []Type - -func (a byUniqueTypeName) Len() int { return len(a) } -func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) } -func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func sortName(t Type) string { - if named := asNamed(t); named != nil { - return named.obj.Id() - } - return "" -} - -func sortMethods(list []*Func) { - sort.Sort(byUniqueMethodName(list)) -} - -func assertSortedMethods(list []*Func) { - if !debug { - panic("internal error: assertSortedMethods called outside debug mode") - } - if !sort.IsSorted(byUniqueMethodName(list)) { - panic("internal error: methods not sorted") - } -} - -// byUniqueMethodName method lists can be sorted by their unique method names. -type byUniqueMethodName []*Func - -func (a byUniqueMethodName) Len() int { return len(a) } -func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() } -func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func (check *Checker) tag(t *ast.BasicLit) string { - if t != nil { - if t.Kind == token.STRING { - if val, err := strconv.Unquote(t.Value); err == nil { - return val - } - } - check.invalidAST(t, "incorrect tag syntax: %q", t.Value) - } - return "" -} - -func (check *Checker) structType(styp *Struct, e *ast.StructType) { - list := e.Fields - if list == nil { - return - } - - // struct fields and tags - var fields []*Var - var tags []string - - // for double-declaration checks - var fset objset - - // current field typ and tag - var typ Type - var tag string - add := func(ident *ast.Ident, embedded bool, pos token.Pos) { - if tag != "" && tags == nil { - tags = make([]string, len(fields)) - } - if tags != nil { - tags = append(tags, tag) - } - - name := ident.Name - fld := NewField(pos, check.pkg, name, typ, embedded) - // spec: "Within a struct, non-blank field names must be unique." - if name == "_" || check.declareInSet(&fset, pos, fld) { - fields = append(fields, fld) - check.recordDef(ident, fld) - } - } - - // addInvalid adds an embedded field of invalid type to the struct for - // fields with errors; this keeps the number of struct fields in sync - // with the source as long as the fields are _ or have different names - // (issue #25627). - addInvalid := func(ident *ast.Ident, pos token.Pos) { - typ = Typ[Invalid] - tag = "" - add(ident, true, pos) - } - - for _, f := range list.List { - typ = check.varType(f.Type) - tag = check.tag(f.Tag) - if len(f.Names) > 0 { - // named fields - for _, name := range f.Names { - add(name, false, name.Pos()) - } - } else { - // embedded field - // spec: "An embedded type must be specified as a type name T or as a - // pointer to a non-interface type name *T, and T itself may not be a - // pointer type." - pos := f.Type.Pos() - name := embeddedFieldIdent(f.Type) - if name == nil { - // TODO(rFindley): using invalidAST here causes test failures (all - // errors should have codes). Clean this up. - check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type) - name = ast.NewIdent("_") - name.NamePos = pos - addInvalid(name, pos) - continue - } - add(name, true, pos) - - // Because we have a name, typ must be of the form T or *T, where T is the name - // of a (named or alias) type, and t (= deref(typ)) must be the type of T. - // We must delay this check to the end because we don't want to instantiate - // (via under(t)) a possibly incomplete type. - - // for use in the closure below - embeddedTyp := typ - embeddedPos := f.Type - - check.later(func() { - t, isPtr := deref(embeddedTyp) - switch t := optype(t).(type) { - case *Basic: - if t == Typ[Invalid] { - // error was reported before - return - } - // unsafe.Pointer is treated like a regular pointer - if t.kind == UnsafePointer { - check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer") - } - case *Pointer: - check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer") - case *Interface: - if isPtr { - check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface") - } - } - }) - } - } - - styp.fields = fields - styp.tags = tags -} - -func embeddedFieldIdent(e ast.Expr) *ast.Ident { - switch e := e.(type) { - case *ast.Ident: - return e - case *ast.StarExpr: - // *T is valid, but **T is not - if _, ok := e.X.(*ast.StarExpr); !ok { - return embeddedFieldIdent(e.X) - } - case *ast.SelectorExpr: - return e.Sel - case *ast.IndexExpr: - return embeddedFieldIdent(e.X) - } - return nil // invalid embedded field -} - -func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []Type { - list := make([]Type, 0, len(types)) // assume all types are correct - for _, texpr := range types { - if texpr == nil { - check.invalidAST(atPos(pos), "missing type constraint") - continue - } - list = append(list, check.varType(texpr)) - } - - // Ensure that each type is only present once in the type list. Types may be - // interfaces, which may not be complete yet. It's ok to do this check at the - // end because it's not a requirement for correctness of the code. - // Note: This is a quadratic algorithm, but type lists tend to be short. - check.later(func() { - for i, t := range list { - if t := asInterface(t); t != nil { - check.completeInterface(types[i].Pos(), t) - } - if includes(list[:i], t) { - check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t) - } - } - }) - - return list -} - -// includes reports whether typ is in list. -func includes(list []Type, typ Type) bool { - for _, e := range list { - if Identical(typ, e) { - return true - } - } - return false -} diff --git a/libgo/go/go/types/unify.go b/libgo/go/go/types/unify.go index db06e21..ad6d316 100644 --- a/libgo/go/go/types/unify.go +++ b/libgo/go/go/types/unify.go @@ -8,8 +8,7 @@ package types import ( "bytes" - "go/token" - "sort" + "fmt" ) // The unifier maintains two separate sets of type parameters x and y @@ -34,14 +33,18 @@ import ( // by setting up one of them (using init) and then assigning its value // to the other. +// Upper limit for recursion depth. Used to catch infinite recursions +// due to implementation issues (e.g., see issues #48619, #48656). +const unificationDepthLimit = 50 + // A unifier maintains the current type parameters for x and y // and the respective types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { - check *Checker exact bool x, y tparamsList // x and y must initialized via tparamsList.init types []Type // inferred types, shared by x and y + depth int // recursion depth during unification } // newUnifier returns a new unifier. @@ -49,8 +52,8 @@ type unifier struct { // exactly. If exact is not set, a named type's underlying type // is considered if unification would fail otherwise, and the // direction of channels is ignored. -func newUnifier(check *Checker, exact bool) *unifier { - u := &unifier{check: check, exact: exact} +func newUnifier(exact bool) *unifier { + u := &unifier{exact: exact} u.x.unifier = u u.y.unifier = u return u @@ -64,7 +67,7 @@ func (u *unifier) unify(x, y Type) bool { // A tparamsList describes a list of type parameters and the types inferred for them. type tparamsList struct { unifier *unifier - tparams []*TypeName + tparams []*TypeParam // For each tparams element, there is a corresponding type slot index in indices. // index < 0: unifier.types[-index-1] == nil // index == 0: no type slot allocated yet @@ -78,29 +81,30 @@ type tparamsList struct { // String returns a string representation for a tparamsList. For debugging. func (d *tparamsList) String() string { var buf bytes.Buffer - buf.WriteByte('[') - for i, tname := range d.tparams { + w := newTypeWriter(&buf, nil) + w.byte('[') + for i, tpar := range d.tparams { if i > 0 { - buf.WriteString(", ") + w.string(", ") } - writeType(&buf, tname.typ, nil, nil) - buf.WriteString(": ") - writeType(&buf, d.at(i), nil, nil) + w.typ(tpar) + w.string(": ") + w.typ(d.at(i)) } - buf.WriteByte(']') + w.byte(']') return buf.String() } // init initializes d with the given type parameters. // The type parameters must be in the order in which they appear in their declaration // (this ensures that the tparams indices match the respective type parameter index). -func (d *tparamsList) init(tparams []*TypeName) { +func (d *tparamsList) init(tparams []*TypeParam) { if len(tparams) == 0 { return } if debug { for i, tpar := range tparams { - assert(i == tpar.typ.(*_TypeParam).index) + assert(i == tpar.index) } } d.tparams = tparams @@ -109,7 +113,7 @@ func (d *tparamsList) init(tparams []*TypeName) { // join unifies the i'th type parameter of x with the j'th type parameter of y. // If both type parameters already have a type associated with them and they are -// not joined, join fails and return false. +// not joined, join fails and returns false. func (u *unifier) join(i, j int) bool { ti := u.x.indices[i] tj := u.y.indices[j] @@ -133,13 +137,18 @@ func (u *unifier) join(i, j int) bool { break case ti > 0 && tj > 0: // Both type parameters have (possibly different) inferred types. Cannot join. + // TODO(gri) Should we check if types are identical? Investigate. return false case ti > 0: // Only the type parameter for x has an inferred type. Use x slot for y. u.y.setIndex(j, ti) + // This case is handled like the default case. + // case tj > 0: + // // Only the type parameter for y has an inferred type. Use y slot for x. + // u.x.setIndex(i, tj) default: - // Either the type parameter for y has an inferred type, or neither type - // parameter has an inferred type. In either case, use y slot for x. + // Neither type parameter has an inferred type. Use y slot for x + // (or x slot for y, it doesn't matter). u.x.setIndex(i, tj) } return true @@ -148,10 +157,23 @@ func (u *unifier) join(i, j int) bool { // If typ is a type parameter of d, index returns the type parameter index. // Otherwise, the result is < 0. func (d *tparamsList) index(typ Type) int { - if t, ok := typ.(*_TypeParam); ok { - if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t { - return i - } + if tpar, ok := typ.(*TypeParam); ok { + return tparamIndex(d.tparams, tpar) + } + return -1 +} + +// If tpar is a type parameter in list, tparamIndex returns the type parameter index. +// Otherwise, the result is < 0. tpar must not be nil.j +func tparamIndex(list []*TypeParam, tpar *TypeParam) int { + // Once a type parameter is bound its index is >= 0. However, there are some + // code paths (namely tracing and type hashing) by which it is possible to + // arrive here with a type parameter that has not been bound, hence the check + // for 0 <= i below. + // TODO(rfindley): investigate a better approach for guarding against using + // unbound type parameters. + if i := tpar.index; 0 <= i && i < len(list) && list[i] == tpar { + return i } return -1 } @@ -216,26 +238,32 @@ func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool { } // nify implements the core unification algorithm which is an -// adapted version of Checker.identical0. For changes to that +// adapted version of Checker.identical. For changes to that // code the corresponding changes should be made here. // Must not be called directly from outside the unifier. func (u *unifier) nify(x, y Type, p *ifacePair) bool { - // types must be expanded for comparison - x = expand(x) - y = expand(y) + // Stop gap for cases where unification fails. + if u.depth >= unificationDepthLimit { + if debug { + panic("unification reached recursion depth limit") + } + return false + } + u.depth++ + defer func() { + u.depth-- + }() if !u.exact { // If exact unification is known to fail because we attempt to // match a type name against an unnamed type literal, consider // the underlying type of the named type. - // (Subtle: We use isNamed to include any type with a name (incl. - // basic types and type parameters. We use asNamed() because we only - // want *Named types.) - switch { - case !isNamed(x) && y != nil && asNamed(y) != nil: - return u.nify(x, under(y), p) - case x != nil && asNamed(x) != nil && !isNamed(y): - return u.nify(under(x), y, p) + // (We use !hasName to exclude any type with a name, including + // basic types and type parameters; the rest are unamed types.) + if nx, _ := x.(*Named); nx != nil && !hasName(y) { + return u.nify(nx.under(), y, p) + } else if ny, _ := y.(*Named); ny != nil && !hasName(x) { + return u.nify(x, ny.under(), p) } } @@ -352,25 +380,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { u.nify(x.results, y.results, p) } - case *_Sum: - // This should not happen with the current internal use of sum types. - panic("type inference across sum types not implemented") - case *Interface: // Two interface types are identical if they have the same set of methods with // the same names and identical function types. Lower-case method names from // different packages are always different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - // If identical0 is called (indirectly) via an external API entry point - // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in - // that case, interfaces are expected to be complete and lazy completion - // here is not needed. - if u.check != nil { - u.check.completeInterface(token.NoPos, x) - u.check.completeInterface(token.NoPos, y) + xset := x.typeSet() + yset := y.typeSet() + if !xset.terms.equal(yset.terms) { + return false } - a := x.allMethods - b := y.allMethods + a := xset.methods + b := yset.methods if len(a) == len(b) { // Interface types are the only types where cycles can occur // that are not "terminated" via named types; and such cycles @@ -402,8 +423,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { p = p.prev } if debug { - assert(sort.IsSorted(byUniqueMethodName(a))) - assert(sort.IsSorted(byUniqueMethodName(b))) + assertSortedMethods(a) + assertSortedMethods(b) } for i, f := range a { g := b[i] @@ -428,19 +449,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { } case *Named: - // Two named types are identical if their type names originate - // in the same type declaration. - // if y, ok := y.(*Named); ok { - // return x.obj == y.obj - // } + // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate. if y, ok := y.(*Named); ok { + xargs := x.targs.list() + yargs := y.targs.list() + // TODO(gri) This is not always correct: two types may have the same names // in the same package if one of them is nested in a function. // Extremely unlikely but we need an always correct solution. if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name { - assert(len(x.targs) == len(y.targs)) - for i, x := range x.targs { - if !u.nify(x, y.targs[i], p) { + assert(len(xargs) == len(yargs)) + for i, x := range xargs { + if !u.nify(x, yargs[i], p) { return false } } @@ -448,21 +468,17 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { } } - case *_TypeParam: + case *TypeParam: // Two type parameters (which are not part of the type parameters of the // enclosing type as those are handled in the beginning of this function) // are identical if they originate in the same declaration. return x == y - // case *instance: - // unreachable since types are expanded - case nil: // avoid a crash in case of nil type default: - u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams) - unreachable() + panic(fmt.Sprintf("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)) } return false diff --git a/libgo/go/go/types/union.go b/libgo/go/go/types/union.go new file mode 100644 index 0000000..9c59279 --- /dev/null +++ b/libgo/go/go/types/union.go @@ -0,0 +1,189 @@ +// 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 types + +import ( + "go/ast" + "go/token" +) + +// ---------------------------------------------------------------------------- +// API + +// A Union represents a union of terms embedded in an interface. +type Union struct { + terms []*Term // list of syntactical terms (not a canonicalized termlist) +} + +// NewUnion returns a new Union type with the given terms. +// It is an error to create an empty union; they are syntactically not possible. +func NewUnion(terms []*Term) *Union { + if len(terms) == 0 { + panic("empty union") + } + return &Union{terms} +} + +func (u *Union) Len() int { return len(u.terms) } +func (u *Union) Term(i int) *Term { return u.terms[i] } + +func (u *Union) Underlying() Type { return u } +func (u *Union) String() string { return TypeString(u, nil) } + +// A Term represents a term in a Union. +type Term term + +// NewTerm returns a new union term. +func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} } + +func (t *Term) Tilde() bool { return t.tilde } +func (t *Term) Type() Type { return t.typ } +func (t *Term) String() string { return (*term)(t).String() } + +// ---------------------------------------------------------------------------- +// Implementation + +// Avoid excessive type-checking times due to quadratic termlist operations. +const maxTermCount = 100 + +// parseUnion parses uexpr as a union of expressions. +// The result is a Union type, or Typ[Invalid] for some errors. +func parseUnion(check *Checker, uexpr ast.Expr) Type { + blist, tlist := flattenUnion(nil, uexpr) + assert(len(blist) == len(tlist)-1) + + var terms []*Term + + var u Type + for i, x := range tlist { + term := parseTilde(check, x) + if len(tlist) == 1 && !term.tilde { + // Single type. Ok to return early because all relevant + // checks have been performed in parseTilde (no need to + // run through term validity check below). + return term.typ // typ already recorded through check.typ in parseTilde + } + if len(terms) >= maxTermCount { + if u != Typ[Invalid] { + check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount) + u = Typ[Invalid] + } + } else { + terms = append(terms, term) + u = &Union{terms} + } + + if i > 0 { + check.recordTypeAndValue(blist[i-1], typexpr, u, nil) + } + } + + if u == Typ[Invalid] { + return u + } + + // Check validity of terms. + // Do this check later because it requires types to be set up. + // Note: This is a quadratic algorithm, but unions tend to be short. + check.later(func() { + for i, t := range terms { + if t.typ == Typ[Invalid] { + continue + } + + u := under(t.typ) + f, _ := u.(*Interface) + if t.tilde { + if f != nil { + check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (%s is an interface)", t.typ) + continue // don't report another error for t + } + + if !Identical(u, t.typ) { + check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u) + continue // don't report another error for t + } + } + + // Stand-alone embedded interfaces are ok and are handled by the single-type case + // in the beginning. Embedded interfaces with tilde are excluded above. If we reach + // here, we must have at least two terms in the union. + if f != nil && !f.typeSet().IsTypeSet() { + switch { + case f.typeSet().NumMethods() != 0: + check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t) + case t.typ == universeComparable.Type(): + check.error(tlist[i], _InvalidUnion, "cannot use comparable in union") + case f.typeSet().comparable: + check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t) + default: + panic("not a type set but no methods and not comparable") + } + continue // don't report another error for t + } + + // Report overlapping (non-disjoint) terms such as + // a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a). + if j := overlappingTerm(terms[:i], t); j >= 0 { + check.softErrorf(tlist[i], _InvalidUnion, "overlapping terms %s and %s", t, terms[j]) + } + } + }) + + return u +} + +func parseTilde(check *Checker, tx ast.Expr) *Term { + x := tx + var tilde bool + if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE { + x = op.X + tilde = true + } + typ := check.typ(x) + // Embedding stand-alone type parameters is not permitted (issue #47127). + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if we embed a lone type parameter, we will + // simply use its underlying type (like we do for other named, embedded interfaces), + // and since the underlying type is an interface the embedding is well defined. + if isTypeParam(typ) { + check.error(x, _MisplacedTypeParam, "cannot embed a type parameter") + typ = Typ[Invalid] + } + term := NewTerm(tilde, typ) + if tilde { + check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil) + } + return term +} + +// overlappingTerm reports the index of the term x in terms which is +// overlapping (not disjoint) from y. The result is < 0 if there is no +// such term. +func overlappingTerm(terms []*Term, y *Term) int { + for i, x := range terms { + // disjoint requires non-nil, non-top arguments + if debug { + if x == nil || x.typ == nil || y == nil || y.typ == nil { + panic("empty or top union term") + } + } + if !(*term)(x).disjoint((*term)(y)) { + return i + } + } + return -1 +} + +// flattenUnion walks a union type expression of the form A | B | C | ..., +// extracting both the binary exprs (blist) and leaf types (tlist). +func flattenUnion(list []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) { + if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR { + blist, tlist = flattenUnion(list, o.X) + blist = append(blist, o) + x = o.Y + } + return blist, append(tlist, x) +} diff --git a/libgo/go/go/types/universe.go b/libgo/go/go/types/universe.go index d7feb2c..3421634 100644 --- a/libgo/go/go/types/universe.go +++ b/libgo/go/go/types/universe.go @@ -8,7 +8,6 @@ package types import ( "go/constant" - "go/internal/typeparams" "go/token" "strings" ) @@ -22,11 +21,12 @@ var Universe *Scope var Unsafe *Package var ( - universeIota *Const - universeByte *Basic // uint8 alias, but has name "byte" - universeRune *Basic // int32 alias, but has name "rune" - universeAny *Interface - universeError *Named + universeIota Object + universeByte Type // uint8 alias, but has name "byte" + universeRune Type // int32 alias, but has name "rune" + universeAny Object + universeError Type + universeComparable Object ) // Typ contains the predeclared *Basic types indexed by their @@ -79,20 +79,43 @@ func defPredeclaredTypes() { def(NewTypeName(token.NoPos, nil, t.name, t)) } - // any - // (Predeclared and entered into universe scope so we do all the - // usual checks; but removed again from scope later since it's - // only visible as constraint in a type parameter list.) - def(NewTypeName(token.NoPos, nil, "any", &emptyInterface)) + // type any = interface{} + // Note: don't use &emptyInterface for the type of any. Using a unique + // pointer allows us to detect any and format it as "any" rather than + // interface{}, which clarifies user-facing error messages significantly. + def(NewTypeName(token.NoPos, nil, "any", &Interface{complete: true, tset: &topTypeSet})) - // Error has a nil package in its qualified name since it is in no package + // type error interface{ Error() string } { + obj := NewTypeName(token.NoPos, nil, "error", nil) + obj.setColor(black) + typ := NewNamed(obj, nil, nil) + + // error.Error() string + recv := NewVar(token.NoPos, nil, "", typ) res := NewVar(token.NoPos, nil, "", Typ[String]) - sig := &Signature{results: NewTuple(res)} + sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false) err := NewFunc(token.NoPos, nil, "Error", sig) - typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()} - sig.recv = NewVar(token.NoPos, nil, "", typ) - def(NewTypeName(token.NoPos, nil, "error", typ)) + + // interface{ Error() string } + ityp := &Interface{obj: obj, methods: []*Func{err}, complete: true} + computeInterfaceTypeSet(nil, token.NoPos, ityp) // prevent races due to lazy computation of tset + + typ.SetUnderlying(ityp) + def(obj) + } + + // type comparable interface{} // marked as comparable + { + obj := NewTypeName(token.NoPos, nil, "comparable", nil) + obj.setColor(black) + typ := NewNamed(obj, nil, nil) + + // interface{} // marked as comparable + ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{true, nil, allTermlist}} + + typ.SetUnderlying(ityp) + def(obj) } } @@ -202,33 +225,6 @@ func DefPredeclaredTestFuncs() { def(newBuiltin(_Trace)) } -func defPredeclaredComparable() { - // The "comparable" interface can be imagined as defined like - // - // type comparable interface { - // == () untyped bool - // != () untyped bool - // } - // - // == and != cannot be user-declared but we can declare - // a magic method == and check for its presence when needed. - - // Define interface { == () }. We don't care about the signature - // for == so leave it empty except for the receiver, which is - // set up later to match the usual interface method assumptions. - sig := new(Signature) - eql := NewFunc(token.NoPos, nil, "==", sig) - iface := NewInterfaceType([]*Func{eql}, nil).Complete() - - // set up the defined type for the interface - obj := NewTypeName(token.NoPos, nil, "comparable", nil) - named := NewNamed(obj, iface, nil) - obj.color_ = black - sig.recv = NewVar(token.NoPos, nil, "", named) // complete == signature - - def(obj) -} - func init() { Universe = NewScope(nil, token.NoPos, token.NoPos, "universe") Unsafe = NewPackage("unsafe", "unsafe") @@ -238,18 +234,13 @@ func init() { defPredeclaredConsts() defPredeclaredNil() defPredeclaredFuncs() - if typeparams.Enabled { - defPredeclaredComparable() - } - - universeIota = Universe.Lookup("iota").(*Const) - universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic) - universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic) - universeAny = Universe.Lookup("any").(*TypeName).typ.(*Interface) - universeError = Universe.Lookup("error").(*TypeName).typ.(*Named) - // "any" is only visible as constraint in a type parameter list - delete(Universe.elems, "any") + universeIota = Universe.Lookup("iota") + universeByte = Universe.Lookup("byte").Type() + universeRune = Universe.Lookup("rune").Type() + universeAny = Universe.Lookup("any") + universeError = Universe.Lookup("error").Type() + universeComparable = Universe.Lookup("comparable") } // Objects with names containing blanks are internal and not entered into @@ -263,7 +254,7 @@ func def(obj Object) { return // nothing to do } // fix Obj link for named types - if typ := asNamed(obj.Type()); typ != nil { + if typ, _ := obj.Type().(*Named); typ != nil { typ.obj = obj.(*TypeName) } // exported identifiers go into package unsafe @@ -281,6 +272,6 @@ func def(obj Object) { } } if scope.Insert(obj) != nil { - panic("internal error: double declaration") + panic("double declaration of predeclared identifier") } } diff --git a/libgo/go/go/types/validtype.go b/libgo/go/go/types/validtype.go new file mode 100644 index 0000000..c4ec2f2 --- /dev/null +++ b/libgo/go/go/types/validtype.go @@ -0,0 +1,147 @@ +// 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 types + +// validType verifies that the given type does not "expand" indefinitely +// producing a cycle in the type graph. Cycles are detected by marking +// defined types. +// (Cycles involving alias types, as in "type A = [10]A" are detected +// earlier, via the objDecl cycle detection mechanism.) +func (check *Checker) validType(typ *Named) { + check.validType0(typ, nil, nil) +} + +type typeInfo uint + +// validType0 checks if the given type is valid. If typ is a type parameter +// its value is looked up in the provided environment. The environment is +// nil if typ is not part of (the RHS of) an instantiated type, in that case +// any type parameter encountered must be from an enclosing function and can +// be ignored. The path is the list of type names that lead to the current typ. +func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo { + const ( + unknown typeInfo = iota + marked + valid + invalid + ) + + switch t := typ.(type) { + case nil: + // We should never see a nil type but be conservative and panic + // only in debug mode. + if debug { + panic("validType0(nil)") + } + + case *Array: + return check.validType0(t.elem, env, path) + + case *Struct: + for _, f := range t.fields { + if check.validType0(f.typ, env, path) == invalid { + return invalid + } + } + + case *Union: + for _, t := range t.terms { + if check.validType0(t.typ, env, path) == invalid { + return invalid + } + } + + case *Interface: + for _, etyp := range t.embeddeds { + if check.validType0(etyp, env, path) == invalid { + return invalid + } + } + + case *Named: + // Don't report a 2nd error if we already know the type is invalid + // (e.g., if a cycle was detected earlier, via under). + if t.underlying == Typ[Invalid] { + check.infoMap[t] = invalid + return invalid + } + + switch check.infoMap[t] { + case unknown: + check.infoMap[t] = marked + check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj)) + case marked: + // We have seen type t before and thus must have a cycle. + check.infoMap[t] = invalid + // t cannot be in an imported package otherwise that package + // would have reported a type cycle and couldn't have been + // imported in the first place. + assert(t.obj.pkg == check.pkg) + t.underlying = Typ[Invalid] // t is in the current package (no race possibilty) + // Find the starting point of the cycle and report it. + for i, tn := range path { + if tn == t.obj { + check.cycleError(path[i:]) + return invalid + } + } + panic("cycle start not found") + } + return check.infoMap[t] + + case *TypeParam: + // A type parameter stands for the type (argument) it was instantiated with. + // Check the corresponding type argument for validity if we have one. + if env != nil { + if targ := env.tmap[t]; targ != nil { + // Type arguments found in targ must be looked + // up in the enclosing environment env.link. + return check.validType0(targ, env.link, path) + } + } + } + + return valid +} + +// A tparamEnv provides the environment for looking up the type arguments +// with which type parameters for a given instance were instantiated. +// If we don't have an instance, the corresponding tparamEnv is nil. +type tparamEnv struct { + tmap substMap + link *tparamEnv +} + +func (env *tparamEnv) push(typ *Named) *tparamEnv { + // If typ is not an instantiated type there are no typ-specific + // type parameters to look up and we don't need an environment. + targs := typ.TypeArgs() + if targs == nil { + return nil // no instance => nil environment + } + + // Populate tmap: remember the type argument for each type parameter. + // We cannot use makeSubstMap because the number of type parameters + // and arguments may not match due to errors in the source (too many + // or too few type arguments). Populate tmap "manually". + tparams := typ.TypeParams() + n, m := targs.Len(), tparams.Len() + if n > m { + n = m // too many targs + } + tmap := make(substMap, n) + for i := 0; i < n; i++ { + tmap[tparams.At(i)] = targs.At(i) + } + + return &tparamEnv{tmap: tmap, link: env} +} + +// TODO(gri) Alternative implementation: +// We may not need to build a stack of environments to +// look up the type arguments for type parameters. The +// same information should be available via the path: +// We should be able to just walk the path backwards +// and find the type arguments in the instance objects. |