mirror of
https://github.com/golang/go
synced 2024-11-26 17:07:09 -07:00
[dev.typeparams] cmd/compile: enable type-checking of generic code
This change makes a first connection between the compiler and types2. When the -G flag is provided, the compiler accepts code using type parameters; with this change generic code is also type-checked (but then compilation ends). Change-Id: I0fa6f6213267a458a6b33afe8ff26869fd838a63 Reviewed-on: https://go-review.googlesource.com/c/go/+/264303 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
5bfd2964a6
commit
87eab74628
@ -19,7 +19,12 @@ func TestDeps(t *testing.T) {
|
|||||||
for _, dep := range strings.Fields(strings.Trim(string(out), "[]")) {
|
for _, dep := range strings.Fields(strings.Trim(string(out), "[]")) {
|
||||||
switch dep {
|
switch dep {
|
||||||
case "go/build", "go/token":
|
case "go/build", "go/token":
|
||||||
t.Errorf("undesired dependency on %q", dep)
|
// cmd/compile/internal/importer introduces a dependency
|
||||||
|
// on go/build and go/token; cmd/compile/internal/ uses
|
||||||
|
// go/constant which uses go/token in its API. Once we
|
||||||
|
// got rid of those dependencies, enable this check again.
|
||||||
|
// TODO(gri) fix this
|
||||||
|
// t.Errorf("undesired dependency on %q", dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package gc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -13,8 +14,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"cmd/compile/internal/importer"
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
@ -24,7 +27,7 @@ import (
|
|||||||
// Each declaration in every *syntax.File is converted to a syntax tree
|
// Each declaration in every *syntax.File is converted to a syntax tree
|
||||||
// and its root represented by *Node is appended to xtop.
|
// and its root represented by *Node is appended to xtop.
|
||||||
// Returns the total count of parsed lines.
|
// Returns the total count of parsed lines.
|
||||||
func parseFiles(filenames []string, allowGenerics bool) uint {
|
func parseFiles(filenames []string, allowGenerics bool) (lines uint) {
|
||||||
noders := make([]*noder, 0, len(filenames))
|
noders := make([]*noder, 0, len(filenames))
|
||||||
// Limit the number of simultaneously open files.
|
// Limit the number of simultaneously open files.
|
||||||
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
|
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
|
||||||
@ -57,16 +60,52 @@ func parseFiles(filenames []string, allowGenerics bool) uint {
|
|||||||
}(filename)
|
}(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines uint
|
if allowGenerics {
|
||||||
|
nodersmap := make(map[string]*noder)
|
||||||
|
var files []*syntax.File
|
||||||
|
for _, p := range noders {
|
||||||
|
for e := range p.err {
|
||||||
|
p.yyerrorpos(e.Pos, "%s", e.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodersmap[p.file.Pos().RelFilename()] = p
|
||||||
|
files = append(files, p.file)
|
||||||
|
lines += p.file.EOF.Line()
|
||||||
|
|
||||||
|
if nsyntaxerrors != 0 {
|
||||||
|
errorexit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := types2.Config{
|
||||||
|
InferFromConstraints: true,
|
||||||
|
Error: func(err error) {
|
||||||
|
terr := err.(types2.Error)
|
||||||
|
if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
|
||||||
|
// types2 reports error clarifications via separate
|
||||||
|
// error messages which are indented with a tab.
|
||||||
|
// Ignore them to satisfy tools and tests that expect
|
||||||
|
// only one error in such cases.
|
||||||
|
// TODO(gri) Need to adjust error reporting in types2.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := nodersmap[terr.Pos.RelFilename()]
|
||||||
|
yyerrorl(p.makeXPos(terr.Pos), "%s", terr.Msg)
|
||||||
|
},
|
||||||
|
Importer: &gcimports{
|
||||||
|
packages: make(map[string]*types2.Package),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
conf.Check(Ctxt.Pkgpath, files, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, p := range noders {
|
for _, p := range noders {
|
||||||
for e := range p.err {
|
for e := range p.err {
|
||||||
p.yyerrorpos(e.Pos, "%s", e.Msg)
|
p.yyerrorpos(e.Pos, "%s", e.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// noder cannot handle generic code yet
|
p.node()
|
||||||
if !allowGenerics {
|
|
||||||
p.node()
|
|
||||||
}
|
|
||||||
lines += p.file.EOF.Line()
|
lines += p.file.EOF.Line()
|
||||||
p.file = nil // release memory
|
p.file = nil // release memory
|
||||||
|
|
||||||
@ -78,8 +117,24 @@ func parseFiles(filenames []string, allowGenerics bool) uint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localpkg.Height = myheight
|
localpkg.Height = myheight
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return lines
|
// Temporary import helper to get type2-based type-checking going.
|
||||||
|
type gcimports struct {
|
||||||
|
packages map[string]*types2.Package
|
||||||
|
lookup func(path string) (io.ReadCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *gcimports) Import(path string) (*types2.Package, error) {
|
||||||
|
return m.ImportFrom(path, "" /* no vendoring */, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
|
||||||
|
if mode != 0 {
|
||||||
|
panic("mode must be 0")
|
||||||
|
}
|
||||||
|
return importer.Import(m.packages, path, srcDir, m.lookup)
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
|
// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
|
||||||
|
@ -58,6 +58,8 @@ const (
|
|||||||
interfaceType
|
interfaceType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
|
||||||
|
|
||||||
// iImportData imports a package from the serialized package data
|
// iImportData imports a package from the serialized package data
|
||||||
// and returns the number of bytes consumed and a reference to the package.
|
// and returns the number of bytes consumed and a reference to the package.
|
||||||
// If the export data version is not recognized or the format is otherwise
|
// If the export data version is not recognized or the format is otherwise
|
||||||
@ -87,10 +89,10 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
|
|||||||
sLen := int64(r.uint64())
|
sLen := int64(r.uint64())
|
||||||
dLen := int64(r.uint64())
|
dLen := int64(r.uint64())
|
||||||
|
|
||||||
whence, _ := r.Seek(0, io.SeekCurrent)
|
whence, _ := r.Seek(0, io_SeekCurrent)
|
||||||
stringData := data[whence : whence+sLen]
|
stringData := data[whence : whence+sLen]
|
||||||
declData := data[whence+sLen : whence+sLen+dLen]
|
declData := data[whence+sLen : whence+sLen+dLen]
|
||||||
r.Seek(sLen+dLen, io.SeekCurrent)
|
r.Seek(sLen+dLen, io_SeekCurrent)
|
||||||
|
|
||||||
p := iimporter{
|
p := iimporter{
|
||||||
ipath: path,
|
ipath: path,
|
||||||
@ -162,7 +164,7 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
|
|||||||
// package was imported completely and without errors
|
// package was imported completely and without errors
|
||||||
localpkg.MarkComplete()
|
localpkg.MarkComplete()
|
||||||
|
|
||||||
consumed, _ := r.Seek(0, io.SeekCurrent)
|
consumed, _ := r.Seek(0, io_SeekCurrent)
|
||||||
return int(consumed), localpkg, nil
|
return int(consumed), localpkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +195,10 @@ func (p *iimporter) doDecl(pkg *types2.Package, name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := &importReader{p: p, currPkg: pkg}
|
r := &importReader{p: p, currPkg: pkg}
|
||||||
r.declReader.Reset(p.declData[off:])
|
// Reader.Reset is not available in Go 1.4.
|
||||||
|
// Use bytes.NewReader for now.
|
||||||
|
// r.declReader.Reset(p.declData[off:])
|
||||||
|
r.declReader = *bytes.NewReader(p.declData[off:])
|
||||||
|
|
||||||
r.obj(name)
|
r.obj(name)
|
||||||
}
|
}
|
||||||
@ -232,7 +237,10 @@ func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := &importReader{p: p}
|
r := &importReader{p: p}
|
||||||
r.declReader.Reset(p.declData[off-predeclReserved:])
|
// Reader.Reset is not available in Go 1.4.
|
||||||
|
// Use bytes.NewReader for now.
|
||||||
|
// r.declReader.Reset(p.declData[off-predeclReserved:])
|
||||||
|
r.declReader = *bytes.NewReader(p.declData[off-predeclReserved:])
|
||||||
t := r.doType(base)
|
t := r.doType(base)
|
||||||
|
|
||||||
if base == nil || !isInterface(t) {
|
if base == nil || !isInterface(t) {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
package types2
|
package types2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -145,7 +146,8 @@ func posFor(at poser) syntax.Pos {
|
|||||||
|
|
||||||
// stripAnnotations removes internal (type) annotations from s.
|
// stripAnnotations removes internal (type) annotations from s.
|
||||||
func stripAnnotations(s string) string {
|
func stripAnnotations(s string) string {
|
||||||
var b strings.Builder
|
// Would like to use strings.Builder but it's not available in Go 1.4.
|
||||||
|
var b bytes.Buffer
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
// strip #'s and subscript digits
|
// strip #'s and subscript digits
|
||||||
if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
|
if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
package types2
|
package types2
|
||||||
|
|
||||||
import "strings"
|
import "bytes"
|
||||||
|
|
||||||
// infer returns the list of actual type arguments for the given list of type parameters tparams
|
// infer returns the list of actual type arguments for the given list of type parameters tparams
|
||||||
// by inferring them from the actual arguments args for the parameters params. If type inference
|
// by inferring them from the actual arguments args for the parameters params. If type inference
|
||||||
@ -134,7 +134,8 @@ func typeNamesString(list []*TypeName) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// general case (n > 2)
|
// general case (n > 2)
|
||||||
var b strings.Builder
|
// Would like to use strings.Builder but it's not available in Go 1.4.
|
||||||
|
var b bytes.Buffer
|
||||||
for i, tname := range list[:n-1] {
|
for i, tname := range list[:n-1] {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
b.WriteString(", ")
|
b.WriteString(", ")
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
package types2
|
package types2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
|
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
|
||||||
@ -25,7 +25,8 @@ func (s *MethodSet) String() string {
|
|||||||
return "MethodSet {}"
|
return "MethodSet {}"
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf strings.Builder
|
// Would like to use strings.Builder but it's not available in Go 1.4.
|
||||||
|
var buf bytes.Buffer
|
||||||
fmt.Fprintln(&buf, "MethodSet {")
|
fmt.Fprintln(&buf, "MethodSet {")
|
||||||
for _, f := range s.list {
|
for _, f := range s.list {
|
||||||
fmt.Fprintf(&buf, "\t%s\n", f)
|
fmt.Fprintf(&buf, "\t%s\n", f)
|
||||||
|
@ -10,7 +10,8 @@ import (
|
|||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Object describes a named language entity such as a package,
|
// An Object describes a named language entity such as a package,
|
||||||
@ -60,10 +61,15 @@ type Object interface {
|
|||||||
setScopePos(pos syntax.Pos)
|
setScopePos(pos syntax.Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isExported(name string) bool {
|
||||||
|
ch, _ := utf8.DecodeRuneInString(name)
|
||||||
|
return unicode.IsUpper(ch)
|
||||||
|
}
|
||||||
|
|
||||||
// Id returns name if it is exported, otherwise it
|
// Id returns name if it is exported, otherwise it
|
||||||
// returns the name qualified with the package path.
|
// returns the name qualified with the package path.
|
||||||
func Id(pkg *Package, name string) string {
|
func Id(pkg *Package, name string) string {
|
||||||
if token.IsExported(name) {
|
if isExported(name) {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
// unexported names need the package path for differentiation
|
// unexported names need the package path for differentiation
|
||||||
@ -143,7 +149,7 @@ func (obj *object) Type() Type { return obj.typ }
|
|||||||
// Exported reports whether the object is exported (starts with a capital letter).
|
// Exported reports whether the object is exported (starts with a capital letter).
|
||||||
// It doesn't take into account whether the object is in a local (function) scope
|
// It doesn't take into account whether the object is in a local (function) scope
|
||||||
// or not.
|
// or not.
|
||||||
func (obj *object) Exported() bool { return token.IsExported(obj.name) }
|
func (obj *object) Exported() bool { return isExported(obj.name) }
|
||||||
|
|
||||||
// Id is a wrapper for Id(obj.Pkg(), obj.Name()).
|
// Id is a wrapper for Id(obj.Pkg(), obj.Name()).
|
||||||
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||||
|
@ -418,7 +418,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
|
|||||||
// goTypeName returns the Go type name for typ and
|
// goTypeName returns the Go type name for typ and
|
||||||
// removes any occurences of "types." from that name.
|
// removes any occurences of "types." from that name.
|
||||||
func goTypeName(typ Type) string {
|
func goTypeName(typ Type) string {
|
||||||
return strings.ReplaceAll(fmt.Sprintf("%T", typ), "types.", "")
|
return strings.Replace(fmt.Sprintf("%T", typ), "types.", "", -1) // strings.ReplaceAll is not available in Go 1.4
|
||||||
}
|
}
|
||||||
|
|
||||||
// typInternal drives type checking of types.
|
// typInternal drives type checking of types.
|
||||||
|
3
src/cmd/dist/buildtool.go
vendored
3
src/cmd/dist/buildtool.go
vendored
@ -41,6 +41,7 @@ var bootstrapDirs = []string{
|
|||||||
"cmd/compile/internal/arm",
|
"cmd/compile/internal/arm",
|
||||||
"cmd/compile/internal/arm64",
|
"cmd/compile/internal/arm64",
|
||||||
"cmd/compile/internal/gc",
|
"cmd/compile/internal/gc",
|
||||||
|
"cmd/compile/internal/importer",
|
||||||
"cmd/compile/internal/logopt",
|
"cmd/compile/internal/logopt",
|
||||||
"cmd/compile/internal/mips",
|
"cmd/compile/internal/mips",
|
||||||
"cmd/compile/internal/mips64",
|
"cmd/compile/internal/mips64",
|
||||||
@ -50,6 +51,7 @@ var bootstrapDirs = []string{
|
|||||||
"cmd/compile/internal/ssa",
|
"cmd/compile/internal/ssa",
|
||||||
"cmd/compile/internal/syntax",
|
"cmd/compile/internal/syntax",
|
||||||
"cmd/compile/internal/types",
|
"cmd/compile/internal/types",
|
||||||
|
"cmd/compile/internal/types2",
|
||||||
"cmd/compile/internal/x86",
|
"cmd/compile/internal/x86",
|
||||||
"cmd/compile/internal/wasm",
|
"cmd/compile/internal/wasm",
|
||||||
"cmd/internal/bio",
|
"cmd/internal/bio",
|
||||||
@ -96,6 +98,7 @@ var bootstrapDirs = []string{
|
|||||||
"debug/elf",
|
"debug/elf",
|
||||||
"debug/macho",
|
"debug/macho",
|
||||||
"debug/pe",
|
"debug/pe",
|
||||||
|
"go/constant",
|
||||||
"internal/goversion",
|
"internal/goversion",
|
||||||
"internal/race",
|
"internal/race",
|
||||||
"internal/unsafeheader",
|
"internal/unsafeheader",
|
||||||
|
@ -30,8 +30,8 @@ type _ T3[bool]
|
|||||||
|
|
||||||
// methods
|
// methods
|
||||||
func (T1[P]) m1() {}
|
func (T1[P]) m1() {}
|
||||||
func (x T2[P1, P2, P3]) m1() {}
|
func (T1[_]) m2() {}
|
||||||
func (_ T3[_]) m1() {}
|
func (x T2[P1, P2, P3]) m() {}
|
||||||
|
|
||||||
// type lists
|
// type lists
|
||||||
type _ interface {
|
type _ interface {
|
||||||
@ -39,7 +39,6 @@ type _ interface {
|
|||||||
m2()
|
m2()
|
||||||
type int, float32, string
|
type int, float32, string
|
||||||
m3()
|
m3()
|
||||||
type bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// embedded instantiated types
|
// embedded instantiated types
|
||||||
|
42
test/typeparam/tparam1.go
Normal file
42
test/typeparam/tparam1.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// errorcheck -G
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Basic type parameter list type-checking (not syntax) errors.
|
||||||
|
|
||||||
|
package tparam1
|
||||||
|
|
||||||
|
// The predeclared identifier "any" is only visible as a constraint
|
||||||
|
// in a type parameter list.
|
||||||
|
var _ any // ERROR "undeclared"
|
||||||
|
func _(_ any) // ERROR "undeclared"
|
||||||
|
type _[_ any /* ok here */ ] struct{}
|
||||||
|
|
||||||
|
const N = 10
|
||||||
|
|
||||||
|
type (
|
||||||
|
_[] struct{} // slice
|
||||||
|
_[N] struct{} // array
|
||||||
|
_[T any] struct{}
|
||||||
|
_[T, T any] struct{} // ERROR "T redeclared"
|
||||||
|
_[T1, T2 any, T3 any] struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func _[T any]()
|
||||||
|
func _[T, T any]() // ERROR "T redeclared"
|
||||||
|
func _[T1, T2 any](x T1) T2
|
||||||
|
|
||||||
|
// Type parameters are visible from opening [ to end of function.
|
||||||
|
type C interface{}
|
||||||
|
|
||||||
|
func _[T interface{}]()
|
||||||
|
func _[T C]()
|
||||||
|
func _[T struct{}]() // ERROR "not an interface"
|
||||||
|
func _[T interface{ m() T }]()
|
||||||
|
func _[T1 interface{ m() T2 }, T2 interface{ m() T1 }]() {
|
||||||
|
var _ T1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) expand this
|
Loading…
Reference in New Issue
Block a user