mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/parser,go/types: hide API changes related to type parameters
While the dev.typeparams branch was merged, the type parameter API is slated for go1.18. Hide these changes to the go/parser and go/types API. This was done as follows: + For APIs that will probably not be needed for go1.18, simply unexport them. + For APIs that we expect to export in go1.18, prefix symbols with '_', so that the planned (upper-cased) symbol name is apparent. + For APIs that must be exported for testing, move both API and tests to files guarded by the go1.18 build constraint. + parser.ParseTypeParams is unexported and copied wherever it is needed. + The -G flag is removed from gofmt, replaced by enabling type parameters if built with the go1.18 build constraint. Notably, changes related to type parameters in go/ast are currently left exported. We're looking at the AST API separately. The new API diff from 1.16 is: +pkg go/ast, method (*FuncDecl) IsMethod() bool +pkg go/ast, method (*ListExpr) End() token.Pos +pkg go/ast, method (*ListExpr) Pos() token.Pos +pkg go/ast, type FuncType struct, TParams *FieldList +pkg go/ast, type ListExpr struct +pkg go/ast, type ListExpr struct, ElemList []Expr +pkg go/ast, type TypeSpec struct, TParams *FieldList +pkg go/types, type Config struct, GoVersion string Change-Id: I1baf67e26279b49092e774309a836c460979774a Reviewed-on: https://go-review.googlesource.com/c/go/+/295929 Trust: Robert Findley <rfindley@google.com> Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
ff5cf4ced3
commit
a6eeb4add4
31 changed files with 370 additions and 304 deletions
|
|
@ -32,7 +32,10 @@ var (
|
||||||
simplifyAST = flag.Bool("s", false, "simplify code")
|
simplifyAST = flag.Bool("s", false, "simplify code")
|
||||||
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
|
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
|
||||||
allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
|
allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
|
||||||
allowTypeParams = flag.Bool("G", false, "allow generic code")
|
|
||||||
|
// allowTypeParams controls whether type parameters are allowed in the code
|
||||||
|
// being formatted. It is enabled for go1.18 in gofmt_go1.18.go.
|
||||||
|
allowTypeParams = false
|
||||||
|
|
||||||
// debugging
|
// debugging
|
||||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
|
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
|
||||||
|
|
@ -48,6 +51,10 @@ const (
|
||||||
//
|
//
|
||||||
// This value is defined in go/printer specifically for go/format and cmd/gofmt.
|
// This value is defined in go/printer specifically for go/format and cmd/gofmt.
|
||||||
printerNormalizeNumbers = 1 << 30
|
printerNormalizeNumbers = 1 << 30
|
||||||
|
|
||||||
|
// parseTypeParams tells go/parser to parse type parameters. Must be kept in
|
||||||
|
// sync with go/parser/interface.go.
|
||||||
|
parseTypeParams parser.Mode = 1 << 30
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -72,8 +79,8 @@ func initParserMode() {
|
||||||
if *allErrors {
|
if *allErrors {
|
||||||
parserMode |= parser.AllErrors
|
parserMode |= parser.AllErrors
|
||||||
}
|
}
|
||||||
if *allowTypeParams {
|
if allowTypeParams {
|
||||||
parserMode |= parser.ParseTypeParams
|
parserMode |= parseTypeParams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
src/cmd/gofmt/gofmt_go1.18.go
Normal file
12
src/cmd/gofmt/gofmt_go1.18.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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 go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
allowTypeParams = true
|
||||||
|
}
|
||||||
|
|
@ -78,7 +78,8 @@ func runTest(t *testing.T, in, out string) {
|
||||||
// fake flag - pretend input is from stdin
|
// fake flag - pretend input is from stdin
|
||||||
stdin = true
|
stdin = true
|
||||||
case "-G":
|
case "-G":
|
||||||
*allowTypeParams = true
|
// fake flag - allow parsing type parameters
|
||||||
|
allowTypeParams = true
|
||||||
default:
|
default:
|
||||||
t.Errorf("unrecognized flag name: %s", name)
|
t.Errorf("unrecognized flag name: %s", name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ func TestErrors(t *testing.T) {
|
||||||
if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
|
if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
|
||||||
mode := DeclarationErrors | AllErrors
|
mode := DeclarationErrors | AllErrors
|
||||||
if strings.HasSuffix(name, ".go2") {
|
if strings.HasSuffix(name, ".go2") {
|
||||||
mode |= ParseTypeParams
|
mode |= parseTypeParams
|
||||||
}
|
}
|
||||||
checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
|
checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,14 @@ const (
|
||||||
Trace // print a trace of parsed productions
|
Trace // print a trace of parsed productions
|
||||||
DeclarationErrors // report declaration errors
|
DeclarationErrors // report declaration errors
|
||||||
SpuriousErrors // same as AllErrors, for backward-compatibility
|
SpuriousErrors // same as AllErrors, for backward-compatibility
|
||||||
ParseTypeParams // Placeholder. Will control the parsing of type parameters.
|
|
||||||
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
|
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
|
||||||
|
|
||||||
|
// parseTypeParams controls the parsing of type parameters. Must be
|
||||||
|
// kept in sync with:
|
||||||
|
// go/printer/printer_test.go
|
||||||
|
// go/types/check_test.go
|
||||||
|
// cmd/gofmt/gofmt.go
|
||||||
|
parseTypeParams = 1 << 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseFile parses the source code of a single Go source file and returns
|
// ParseFile parses the source code of a single Go source file and returns
|
||||||
|
|
|
||||||
|
|
@ -651,7 +651,7 @@ func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := p.parseTypeName(ident)
|
typ := p.parseTypeName(ident)
|
||||||
if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
|
if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
|
||||||
typ = p.parseTypeInstance(typ)
|
typ = p.parseTypeInstance(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -712,7 +712,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
|
||||||
// TODO(rfindley): consider changing parseRhsOrType so that this function variable
|
// TODO(rfindley): consider changing parseRhsOrType so that this function variable
|
||||||
// is not needed.
|
// is not needed.
|
||||||
argparser := p.parseRhsOrType
|
argparser := p.parseRhsOrType
|
||||||
if p.mode&ParseTypeParams == 0 {
|
if p.mode&parseTypeParams == 0 {
|
||||||
argparser = p.parseRhs
|
argparser = p.parseRhs
|
||||||
}
|
}
|
||||||
if p.tok != token.RBRACK {
|
if p.tok != token.RBRACK {
|
||||||
|
|
@ -742,13 +742,13 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
|
||||||
// x [P]E
|
// x [P]E
|
||||||
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
|
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
|
||||||
}
|
}
|
||||||
if p.mode&ParseTypeParams == 0 {
|
if p.mode&parseTypeParams == 0 {
|
||||||
p.error(rbrack, "missing element type in array type expression")
|
p.error(rbrack, "missing element type in array type expression")
|
||||||
return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
|
return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.mode&ParseTypeParams == 0 {
|
if p.mode&parseTypeParams == 0 {
|
||||||
p.error(firstComma, "expected ']', found ','")
|
p.error(firstComma, "expected ']', found ','")
|
||||||
return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
|
return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
|
||||||
}
|
}
|
||||||
|
|
@ -1045,7 +1045,7 @@ func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams,
|
||||||
defer un(trace(p, "Parameters"))
|
defer un(trace(p, "Parameters"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.mode&ParseTypeParams != 0 && acceptTParams && p.tok == token.LBRACK {
|
if p.mode&parseTypeParams != 0 && acceptTParams && p.tok == token.LBRACK {
|
||||||
opening := p.pos
|
opening := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
// [T any](params) syntax
|
// [T any](params) syntax
|
||||||
|
|
@ -1119,7 +1119,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
|
||||||
x := p.parseTypeName(nil)
|
x := p.parseTypeName(nil)
|
||||||
if ident, _ := x.(*ast.Ident); ident != nil {
|
if ident, _ := x.(*ast.Ident); ident != nil {
|
||||||
switch {
|
switch {
|
||||||
case p.tok == token.LBRACK && p.mode&ParseTypeParams != 0:
|
case p.tok == token.LBRACK && p.mode&parseTypeParams != 0:
|
||||||
// generic method or embedded instantiated type
|
// generic method or embedded instantiated type
|
||||||
lbrack := p.pos
|
lbrack := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
|
|
@ -1171,7 +1171,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
|
||||||
} else {
|
} else {
|
||||||
// embedded, possibly instantiated type
|
// embedded, possibly instantiated type
|
||||||
typ = x
|
typ = x
|
||||||
if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
|
if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
|
||||||
// embedded instantiated interface
|
// embedded instantiated interface
|
||||||
typ = p.parseTypeInstance(typ)
|
typ = p.parseTypeInstance(typ)
|
||||||
}
|
}
|
||||||
|
|
@ -1193,7 +1193,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
scope := ast.NewScope(nil) // interface scope
|
scope := ast.NewScope(nil) // interface scope
|
||||||
var list []*ast.Field
|
var list []*ast.Field
|
||||||
for p.tok == token.IDENT || p.mode&ParseTypeParams != 0 && p.tok == token.TYPE {
|
for p.tok == token.IDENT || p.mode&parseTypeParams != 0 && p.tok == token.TYPE {
|
||||||
if p.tok == token.IDENT {
|
if p.tok == token.IDENT {
|
||||||
list = append(list, p.parseMethodSpec(scope))
|
list = append(list, p.parseMethodSpec(scope))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1289,7 +1289,7 @@ func (p *parser) tryIdentOrType() ast.Expr {
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case token.IDENT:
|
case token.IDENT:
|
||||||
typ := p.parseTypeName(nil)
|
typ := p.parseTypeName(nil)
|
||||||
if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
|
if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
|
||||||
typ = p.parseTypeInstance(typ)
|
typ = p.parseTypeInstance(typ)
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
|
|
@ -1552,7 +1552,7 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr {
|
||||||
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
|
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.mode&ParseTypeParams == 0 {
|
if p.mode&parseTypeParams == 0 {
|
||||||
p.error(firstComma, "expected ']' or ':', found ','")
|
p.error(firstComma, "expected ']' or ':', found ','")
|
||||||
return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
|
return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
|
||||||
}
|
}
|
||||||
|
|
@ -2696,7 +2696,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
|
||||||
p.exprLev++
|
p.exprLev++
|
||||||
x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
|
x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
|
||||||
p.exprLev--
|
p.exprLev--
|
||||||
if name0, _ := x.(*ast.Ident); p.mode&ParseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK {
|
if name0, _ := x.(*ast.Ident); p.mode&parseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK {
|
||||||
// generic type [T any];
|
// generic type [T any];
|
||||||
p.parseGenericType(spec, lbrack, name0, token.RBRACK)
|
p.parseGenericType(spec, lbrack, name0, token.RBRACK)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,8 @@ var valids = []string{
|
||||||
}
|
}
|
||||||
|
|
||||||
// validWithTParamsOnly holds source code examples that are valid if
|
// validWithTParamsOnly holds source code examples that are valid if
|
||||||
// ParseTypeParams is set, but invalid if not. When checking with the
|
// parseTypeParams is set, but invalid if not. When checking with the
|
||||||
// ParseTypeParams set, errors are ignored.
|
// parseTypeParams set, errors are ignored.
|
||||||
var validWithTParamsOnly = []string{
|
var validWithTParamsOnly = []string{
|
||||||
`package p; type _ []T[ /* ERROR "expected ';', found '\['" */ int]`,
|
`package p; type _ []T[ /* ERROR "expected ';', found '\['" */ int]`,
|
||||||
`package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`,
|
`package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`,
|
||||||
|
|
@ -131,10 +131,10 @@ func TestValid(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("tparams", func(t *testing.T) {
|
t.Run("tparams", func(t *testing.T) {
|
||||||
for _, src := range valids {
|
for _, src := range valids {
|
||||||
checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, false)
|
checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, false)
|
||||||
}
|
}
|
||||||
for _, src := range validWithTParamsOnly {
|
for _, src := range validWithTParamsOnly {
|
||||||
checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, false)
|
checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +142,7 @@ func TestValid(t *testing.T) {
|
||||||
// TestSingle is useful to track down a problem with a single short test program.
|
// TestSingle is useful to track down a problem with a single short test program.
|
||||||
func TestSingle(t *testing.T) {
|
func TestSingle(t *testing.T) {
|
||||||
const src = `package p; var _ = T[P]{}`
|
const src = `package p; var _ = T[P]{}`
|
||||||
checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
|
checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var invalids = []string{
|
var invalids = []string{
|
||||||
|
|
@ -261,10 +261,10 @@ func TestInvalid(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("tparams", func(t *testing.T) {
|
t.Run("tparams", func(t *testing.T) {
|
||||||
for _, src := range invalids {
|
for _, src := range invalids {
|
||||||
checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
|
checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
|
||||||
}
|
}
|
||||||
for _, src := range invalidTParamErrs {
|
for _, src := range invalidTParamErrs {
|
||||||
checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
|
checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// parseTypeParams tells go/parser to parse type parameters. Must be kept in
|
||||||
|
// sync with go/parser/interface.go.
|
||||||
|
const parseTypeParams parser.Mode = 1 << 30
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dataDir = "testdata"
|
dataDir = "testdata"
|
||||||
tabwidth = 8
|
tabwidth = 8
|
||||||
|
|
@ -35,6 +39,7 @@ const (
|
||||||
rawFormat
|
rawFormat
|
||||||
normNumber
|
normNumber
|
||||||
idempotent
|
idempotent
|
||||||
|
allowTypeParams
|
||||||
)
|
)
|
||||||
|
|
||||||
// format parses src, prints the corresponding AST, verifies the resulting
|
// format parses src, prints the corresponding AST, verifies the resulting
|
||||||
|
|
@ -42,7 +47,11 @@ const (
|
||||||
// if any.
|
// if any.
|
||||||
func format(src []byte, mode checkMode) ([]byte, error) {
|
func format(src []byte, mode checkMode) ([]byte, error) {
|
||||||
// parse src
|
// parse src
|
||||||
f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.ParseTypeParams)
|
parseMode := parser.ParseComments
|
||||||
|
if mode&allowTypeParams != 0 {
|
||||||
|
parseMode |= parseTypeParams
|
||||||
|
}
|
||||||
|
f, err := parser.ParseFile(fset, "", src, parseMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parse: %s\n%s", err, src)
|
return nil, fmt.Errorf("parse: %s\n%s", err, src)
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +79,7 @@ func format(src []byte, mode checkMode) ([]byte, error) {
|
||||||
|
|
||||||
// make sure formatted output is syntactically correct
|
// make sure formatted output is syntactically correct
|
||||||
res := buf.Bytes()
|
res := buf.Bytes()
|
||||||
if _, err := parser.ParseFile(fset, "", res, parser.ParseTypeParams); err != nil {
|
if _, err := parser.ParseFile(fset, "", res, parseTypeParams); err != nil {
|
||||||
return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
|
return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,13 +210,13 @@ var data = []entry{
|
||||||
{"linebreaks.input", "linebreaks.golden", idempotent},
|
{"linebreaks.input", "linebreaks.golden", idempotent},
|
||||||
{"expressions.input", "expressions.golden", idempotent},
|
{"expressions.input", "expressions.golden", idempotent},
|
||||||
{"expressions.input", "expressions.raw", rawFormat | idempotent},
|
{"expressions.input", "expressions.raw", rawFormat | idempotent},
|
||||||
{"declarations.input", "declarations.golden", 0},
|
{"declarations.input", "declarations.golden", allowTypeParams},
|
||||||
{"statements.input", "statements.golden", 0},
|
{"statements.input", "statements.golden", 0},
|
||||||
{"slow.input", "slow.golden", idempotent},
|
{"slow.input", "slow.golden", idempotent},
|
||||||
{"complit.input", "complit.x", export},
|
{"complit.input", "complit.x", export},
|
||||||
{"go2numbers.input", "go2numbers.golden", idempotent},
|
{"go2numbers.input", "go2numbers.golden", idempotent},
|
||||||
{"go2numbers.input", "go2numbers.norm", normNumber | idempotent},
|
{"go2numbers.input", "go2numbers.norm", normNumber | idempotent},
|
||||||
{"generics.input", "generics.golden", idempotent},
|
{"generics.input", "generics.golden", idempotent | allowTypeParams},
|
||||||
{"gobuild1.input", "gobuild1.golden", idempotent},
|
{"gobuild1.input", "gobuild1.golden", idempotent},
|
||||||
{"gobuild2.input", "gobuild2.golden", idempotent},
|
{"gobuild2.input", "gobuild2.golden", idempotent},
|
||||||
{"gobuild3.input", "gobuild3.golden", idempotent},
|
{"gobuild3.input", "gobuild3.golden", idempotent},
|
||||||
|
|
|
||||||
|
|
@ -184,11 +184,11 @@ type Info struct {
|
||||||
// qualified identifiers are collected in the Uses map.
|
// qualified identifiers are collected in the Uses map.
|
||||||
Types map[ast.Expr]TypeAndValue
|
Types map[ast.Expr]TypeAndValue
|
||||||
|
|
||||||
// Inferred maps calls of parameterized functions that use
|
// _Inferred maps calls of parameterized functions that use
|
||||||
// type inference to the inferred type arguments and signature
|
// type inference to the _Inferred type arguments and signature
|
||||||
// of the function called. The recorded "call" expression may be
|
// 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]).
|
// an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
|
||||||
Inferred map[ast.Expr]Inferred
|
_Inferred map[ast.Expr]_Inferred
|
||||||
|
|
||||||
// Defs maps identifiers to the objects they define (including
|
// Defs maps identifiers to the objects they define (including
|
||||||
// package names, dots "." of dot-imports, and blank "_" identifiers).
|
// package names, dots "." of dot-imports, and blank "_" identifiers).
|
||||||
|
|
@ -346,9 +346,9 @@ func (tv TypeAndValue) HasOk() bool {
|
||||||
return tv.mode == commaok || tv.mode == mapindex
|
return tv.mode == commaok || tv.mode == mapindex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inferred reports the inferred type arguments and signature
|
// _Inferred reports the _Inferred type arguments and signature
|
||||||
// for a parameterized function call that uses type inference.
|
// for a parameterized function call that uses type inference.
|
||||||
type Inferred struct {
|
type _Inferred struct {
|
||||||
Targs []Type
|
Targs []Type
|
||||||
Sig *Signature
|
Sig *Signature
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
src/go/types/api_go1.18.go
Normal file
22
src/go/types/api_go1.18.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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 go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Inferred = _Inferred
|
||||||
|
|
||||||
|
func GetInferred(info *Info) map[ast.Expr]Inferred {
|
||||||
|
return info._Inferred
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetInferred(info *Info, inferred map[ast.Expr]Inferred) {
|
||||||
|
info._Inferred = inferred
|
||||||
|
}
|
||||||
138
src/go/types/api_go1.18_test.go
Normal file
138
src/go/types/api_go1.18_test.go
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
// 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 go1.18
|
||||||
|
|
||||||
|
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{}
|
||||||
|
SetInferred(&info, 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 GetInferred(&info) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ const genericPkg = "package generic_"
|
||||||
|
|
||||||
func modeForSource(src string) parser.Mode {
|
func modeForSource(src string) parser.Mode {
|
||||||
if strings.HasPrefix(src, genericPkg) {
|
if strings.HasPrefix(src, genericPkg) {
|
||||||
return parser.ParseTypeParams
|
return parseTypeParams
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
@ -377,128 +377,6 @@ func TestTypesInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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{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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefsInfo(t *testing.T) {
|
func TestDefsInfo(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
src string
|
src string
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
mode = value
|
mode = value
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
if t.is(func(t Type) bool {
|
if t.is(func(t Type) bool {
|
||||||
switch t := under(t).(type) {
|
switch t := under(t).(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
|
|
@ -469,7 +469,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
m = 2
|
m = 2
|
||||||
case *Map, *Chan:
|
case *Map, *Chan:
|
||||||
m = 1
|
m = 1
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
return t.is(valid)
|
return t.is(valid)
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
@ -732,8 +732,8 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||||
|
|
||||||
// construct a suitable new type parameter
|
// construct a suitable new type parameter
|
||||||
tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
|
tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
|
||||||
ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
|
ptyp := check.newTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
|
||||||
tsum := NewSum(rtypes)
|
tsum := _NewSum(rtypes)
|
||||||
ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
|
ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
|
||||||
|
|
||||||
return ptyp
|
return ptyp
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func (check *Checker) call(x *operand, call *ast.CallExpr) exprKind {
|
||||||
}
|
}
|
||||||
if t := asInterface(T); t != nil {
|
if t := asInterface(T); t != nil {
|
||||||
check.completeInterface(token.NoPos, t)
|
check.completeInterface(token.NoPos, t)
|
||||||
if t.IsConstraint() {
|
if t._IsConstraint() {
|
||||||
check.errorf(call, _Todo, "cannot use interface %s in conversion (contains type list or is comparable)", T)
|
check.errorf(call, _Todo, "cannot use interface %s in conversion (contains type list or is comparable)", T)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,8 +396,8 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
|
||||||
func (check *Checker) recordInferred(call ast.Expr, targs []Type, sig *Signature) {
|
func (check *Checker) recordInferred(call ast.Expr, targs []Type, sig *Signature) {
|
||||||
assert(call != nil)
|
assert(call != nil)
|
||||||
assert(sig != nil)
|
assert(sig != nil)
|
||||||
if m := check.Inferred; m != nil {
|
if m := check._Inferred; m != nil {
|
||||||
m[call] = Inferred{targs, sig}
|
m[call] = _Inferred{targs, sig}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ import (
|
||||||
. "go/types"
|
. "go/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// parseTypeParams tells go/parser to parse type parameters. Must be kept in
|
||||||
|
// sync with go/parser/interface.go.
|
||||||
|
const parseTypeParams parser.Mode = 1 << 30
|
||||||
|
|
||||||
var (
|
var (
|
||||||
haltOnError = flag.Bool("halt", false, "halt on error")
|
haltOnError = flag.Bool("halt", false, "halt on error")
|
||||||
listErrors = flag.Bool("errlist", false, "list errors")
|
listErrors = flag.Bool("errlist", false, "list errors")
|
||||||
|
|
@ -210,7 +214,7 @@ func checkFiles(t *testing.T, goVersion string, filenames []string, srcs [][]byt
|
||||||
|
|
||||||
mode := parser.AllErrors
|
mode := parser.AllErrors
|
||||||
if strings.HasSuffix(filenames[0], ".go2") {
|
if strings.HasSuffix(filenames[0], ".go2") {
|
||||||
mode |= parser.ParseTypeParams
|
mode |= parseTypeParams
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse files and collect parser errors
|
// parse files and collect parser errors
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
|
||||||
|
|
||||||
setBoundAt := func(at int, bound Type) {
|
setBoundAt := func(at int, bound Type) {
|
||||||
assert(IsInterface(bound))
|
assert(IsInterface(bound))
|
||||||
tparams[at].typ.(*TypeParam).bound = bound
|
tparams[at].typ.(*_TypeParam).bound = bound
|
||||||
}
|
}
|
||||||
|
|
||||||
index := 0
|
index := 0
|
||||||
|
|
@ -756,7 +756,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
|
||||||
func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName {
|
func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
||||||
check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
|
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
|
check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
|
||||||
tparams = append(tparams, tpar)
|
tparams = append(tparams, tpar)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -660,7 +660,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
|
||||||
default:
|
default:
|
||||||
return nil, nil, _InvalidUntypedConversion
|
return nil, nil, _InvalidUntypedConversion
|
||||||
}
|
}
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
ok := t.is(func(t Type) bool {
|
ok := t.is(func(t Type) bool {
|
||||||
target, _, _ := check.implicitTypeAndValue(x, t)
|
target, _, _ := check.implicitTypeAndValue(x, t)
|
||||||
return target != nil
|
return target != nil
|
||||||
|
|
@ -1517,7 +1517,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
x.expr = e
|
x.expr = e
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
// A sum type can be indexed if all of the sum's types
|
// A sum type can be indexed if all of the sum's types
|
||||||
// support indexing and have the same index and element
|
// support indexing and have the same index and element
|
||||||
// type. Special rules apply for maps in the sum type.
|
// type. Special rules apply for maps in the sum type.
|
||||||
|
|
@ -1549,7 +1549,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
tkey = t.key
|
tkey = t.key
|
||||||
e = t.elem
|
e = t.elem
|
||||||
nmaps++
|
nmaps++
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
|
check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
|
||||||
case *instance:
|
case *instance:
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
|
|
@ -1661,7 +1661,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
valid = true
|
valid = true
|
||||||
// x.typ doesn't change
|
// x.typ doesn't change
|
||||||
|
|
||||||
case *Sum, *TypeParam:
|
case *_Sum, *_TypeParam:
|
||||||
check.errorf(x, 0, "generic slice expressions not yet implemented")
|
check.errorf(x, 0, "generic slice expressions not yet implemented")
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (check *Checker) infer(tparams []*TypeName, params *Tuple, args []*operand)
|
||||||
// only parameter type it can possibly match against is a *TypeParam.
|
// only parameter type it can possibly match against is a *TypeParam.
|
||||||
// Thus, only keep the indices of generic parameters that are not of
|
// Thus, only keep the indices of generic parameters that are not of
|
||||||
// composite types and which don't have a type inferred yet.
|
// composite types and which don't have a type inferred yet.
|
||||||
if tpar, _ := par.typ.(*TypeParam); tpar != nil && u.x.at(tpar.index) == nil {
|
if tpar, _ := par.typ.(*_TypeParam); tpar != nil && u.x.at(tpar.index) == nil {
|
||||||
indices[j] = i
|
indices[j] = i
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +201,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
return w.isParameterizedList(t.types)
|
return w.isParameterizedList(t.types)
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
|
|
@ -243,7 +243,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
|
||||||
case *Named:
|
case *Named:
|
||||||
return w.isParameterizedList(t.targs)
|
return w.isParameterizedList(t.targs)
|
||||||
|
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
// t must be one of w.tparams
|
// t must be one of w.tparams
|
||||||
return t.index < len(w.tparams) && w.tparams[t.index].typ == t
|
return t.index < len(w.tparams) && w.tparams[t.index].typ == t
|
||||||
|
|
||||||
|
|
@ -291,7 +291,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type) (types []Type, i
|
||||||
|
|
||||||
// Unify type parameters with their structural constraints, if any.
|
// Unify type parameters with their structural constraints, if any.
|
||||||
for _, tpar := range tparams {
|
for _, tpar := range tparams {
|
||||||
typ := tpar.typ.(*TypeParam)
|
typ := tpar.typ.(*_TypeParam)
|
||||||
sbound := check.structuralType(typ.bound)
|
sbound := check.structuralType(typ.bound)
|
||||||
if sbound != nil {
|
if sbound != nil {
|
||||||
if !u.unify(typ, sbound) {
|
if !u.unify(typ, sbound) {
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
|
||||||
var next []embeddedType // embedded types found at current depth
|
var next []embeddedType // embedded types found at current depth
|
||||||
|
|
||||||
// look for (pkg, name) in all types at current depth
|
// look for (pkg, name) in all types at current depth
|
||||||
var tpar *TypeParam // set if obj receiver is a type parameter
|
var tpar *_TypeParam // set if obj receiver is a type parameter
|
||||||
for _, e := range current {
|
for _, e := range current {
|
||||||
typ := e.typ
|
typ := e.typ
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
|
||||||
indirect = e.indirect
|
indirect = e.indirect
|
||||||
}
|
}
|
||||||
|
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
// only consider explicit methods in the type parameter bound, not
|
// only consider explicit methods in the type parameter bound, not
|
||||||
// methods that may be common to all types in the type list.
|
// methods that may be common to all types in the type list.
|
||||||
if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
|
if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
|
||||||
|
|
||||||
// x is an untyped value representable by a value of type T.
|
// x is an untyped value representable by a value of type T.
|
||||||
if isUntyped(Vu) {
|
if isUntyped(Vu) {
|
||||||
if t, ok := Tu.(*Sum); ok {
|
if t, ok := Tu.(*_Sum); ok {
|
||||||
return t.is(func(t Type) bool {
|
return t.is(func(t Type) bool {
|
||||||
// TODO(gri) this could probably be more efficient
|
// TODO(gri) this could probably be more efficient
|
||||||
ok, _ := x.assignableTo(check, t, reason)
|
ok, _ := x.assignableTo(check, t, reason)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// isNamed may be called with types that are not fully set up.
|
// isNamed may be called with types that are not fully set up.
|
||||||
func isNamed(typ Type) bool {
|
func isNamed(typ Type) bool {
|
||||||
switch typ.(type) {
|
switch typ.(type) {
|
||||||
case *Basic, *Named, *TypeParam, *instance:
|
case *Basic, *Named, *_TypeParam, *instance:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -32,7 +32,7 @@ func is(typ Type, what BasicInfo) bool {
|
||||||
switch t := optype(typ).(type) {
|
switch t := optype(typ).(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
return t.info&what != 0
|
return t.info&what != 0
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
return t.is(func(typ Type) bool { return is(typ, what) })
|
return t.is(func(typ Type) bool { return is(typ, what) })
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -109,7 +109,7 @@ func comparable(T Type, seen map[Type]bool) bool {
|
||||||
//
|
//
|
||||||
// is not comparable because []byte is not comparable.
|
// is not comparable because []byte is not comparable.
|
||||||
if t := asTypeParam(T); t != nil && optype(t) == theTop {
|
if t := asTypeParam(T); t != nil && optype(t) == theTop {
|
||||||
return t.Bound().IsComparable()
|
return t.Bound()._IsComparable()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := optype(T).(type) {
|
switch t := optype(T).(type) {
|
||||||
|
|
@ -128,13 +128,13 @@ func comparable(T Type, seen map[Type]bool) bool {
|
||||||
return true
|
return true
|
||||||
case *Array:
|
case *Array:
|
||||||
return comparable(t.elem, seen)
|
return comparable(t.elem, seen)
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
pred := func(t Type) bool {
|
pred := func(t Type) bool {
|
||||||
return comparable(t, seen)
|
return comparable(t, seen)
|
||||||
}
|
}
|
||||||
return t.is(pred)
|
return t.is(pred)
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
return t.Bound().IsComparable()
|
return t.Bound()._IsComparable()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +146,7 @@ func hasNil(typ Type) bool {
|
||||||
return t.kind == UnsafePointer
|
return t.kind == UnsafePointer
|
||||||
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
||||||
return true
|
return true
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
return t.is(hasNil)
|
return t.is(hasNil)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -265,14 +265,14 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||||
check.identical0(x.results, y.results, cmpTags, p)
|
check.identical0(x.results, y.results, cmpTags, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
// Two sum types are identical if they contain the same types.
|
// Two sum types are identical if they contain the same types.
|
||||||
// (Sum types always consist of at least two types. Also, the
|
// (Sum types always consist of at least two types. Also, the
|
||||||
// the set (list) of types in a sum type consists of unique
|
// the set (list) of types in a sum type consists of unique
|
||||||
// types - each type appears exactly once. Thus, two sum types
|
// types - each type appears exactly once. Thus, two sum types
|
||||||
// must contain the same number of types to have chance of
|
// must contain the same number of types to have chance of
|
||||||
// being equal.
|
// being equal.
|
||||||
if y, ok := y.(*Sum); ok && len(x.types) == len(y.types) {
|
if y, ok := y.(*_Sum); ok && len(x.types) == len(y.types) {
|
||||||
// Every type in x.types must be in y.types.
|
// Every type in x.types must be in y.types.
|
||||||
// Quadratic algorithm, but probably good enough for now.
|
// Quadratic algorithm, but probably good enough for now.
|
||||||
// TODO(gri) we need a fast quick type ID/hash for all types.
|
// TODO(gri) we need a fast quick type ID/hash for all types.
|
||||||
|
|
@ -370,7 +370,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||||
return x.obj == y.obj
|
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)
|
// nothing to do (x and y being equal is caught in the very beginning of this function)
|
||||||
|
|
||||||
// case *instance:
|
// case *instance:
|
||||||
|
|
@ -397,7 +397,7 @@ func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifaceP
|
||||||
}
|
}
|
||||||
for i, x := range x {
|
for i, x := range x {
|
||||||
y := y[i]
|
y := y[i]
|
||||||
if !check.identical0(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) {
|
if !check.identical0(x.typ.(*_TypeParam).bound, y.typ.(*_TypeParam).bound, cmpTags, p) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func sanitizeInfo(info *Info) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for e, inf := range info.Inferred {
|
for e, inf := range info._Inferred {
|
||||||
changed := false
|
changed := false
|
||||||
for i, targ := range inf.Targs {
|
for i, targ := range inf.Targs {
|
||||||
if typ := s.typ(targ); typ != targ {
|
if typ := s.typ(targ); typ != targ {
|
||||||
|
|
@ -37,7 +37,7 @@ func sanitizeInfo(info *Info) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
info.Inferred[e] = inf
|
info._Inferred[e] = inf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ func (s sanitizer) typ(typ Type) Type {
|
||||||
s.tuple(t.params)
|
s.tuple(t.params)
|
||||||
s.tuple(t.results)
|
s.tuple(t.results)
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
s.typeList(t.types)
|
s.typeList(t.types)
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
|
|
@ -135,7 +135,7 @@ func (s sanitizer) typ(typ Type) Type {
|
||||||
s.typeList(t.targs)
|
s.typeList(t.targs)
|
||||||
s.funcList(t.methods)
|
s.funcList(t.methods)
|
||||||
|
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
if bound := s.typ(t.bound); bound != t.bound {
|
if bound := s.typ(t.bound); bound != t.bound {
|
||||||
t.bound = bound
|
t.bound = bound
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,13 +108,13 @@ func (s *Scope) Insert(obj Object) Object {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Squash merges s with its parent scope p by adding all
|
// squash merges s with its parent scope p by adding all
|
||||||
// objects of s to p, adding all children of s to the
|
// objects of s to p, adding all children of s to the
|
||||||
// children of p, and removing s from p's children.
|
// children of p, and removing s from p's children.
|
||||||
// The function f is called for each object obj in s which
|
// The function f is called for each object obj in s which
|
||||||
// has an object alt in p. s should be discarded after
|
// has an object alt in p. s should be discarded after
|
||||||
// having been squashed.
|
// having been squashed.
|
||||||
func (s *Scope) Squash(err func(obj, alt Object)) {
|
func (s *Scope) squash(err func(obj, alt Object)) {
|
||||||
p := s.parent
|
p := s.parent
|
||||||
assert(p != nil)
|
assert(p != nil)
|
||||||
for _, obj := range s.elems {
|
for _, obj := range s.elems {
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ func (s *StdSizes) Sizeof(T Type) int64 {
|
||||||
}
|
}
|
||||||
offsets := s.Offsetsof(t.fields)
|
offsets := s.Offsetsof(t.fields)
|
||||||
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
|
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
panic("Sizeof unimplemented for type sum")
|
panic("Sizeof unimplemented for type sum")
|
||||||
case *Interface:
|
case *Interface:
|
||||||
return s.WordSize * 2
|
return s.WordSize * 2
|
||||||
|
|
|
||||||
|
|
@ -907,7 +907,7 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
|
||||||
msg = "send-only channel"
|
msg = "send-only channel"
|
||||||
}
|
}
|
||||||
return typ.elem, Typ[Invalid], msg
|
return typ.elem, Typ[Invalid], msg
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
first := true
|
first := true
|
||||||
var key, val Type
|
var key, val Type
|
||||||
var msg string
|
var msg string
|
||||||
|
|
|
||||||
|
|
@ -22,21 +22,21 @@ type substMap struct {
|
||||||
// TODO(gri) rewrite that code, get rid of this field, and make this
|
// TODO(gri) rewrite that code, get rid of this field, and make this
|
||||||
// struct just the map (proj)
|
// struct just the map (proj)
|
||||||
targs []Type
|
targs []Type
|
||||||
proj map[*TypeParam]Type
|
proj map[*_TypeParam]Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
|
// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
|
||||||
// If targs[i] is nil, tpars[i] is not substituted.
|
// If targs[i] is nil, tpars[i] is not substituted.
|
||||||
func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
|
func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
|
||||||
assert(len(tpars) == len(targs))
|
assert(len(tpars) == len(targs))
|
||||||
proj := make(map[*TypeParam]Type, len(tpars))
|
proj := make(map[*_TypeParam]Type, len(tpars))
|
||||||
for i, tpar := range tpars {
|
for i, tpar := range tpars {
|
||||||
// We must expand type arguments otherwise *instance
|
// We must expand type arguments otherwise *instance
|
||||||
// types end up as components in composite types.
|
// types end up as components in composite types.
|
||||||
// TODO(gri) explain why this causes problems, if it does
|
// TODO(gri) explain why this causes problems, if it does
|
||||||
targ := expand(targs[i]) // possibly nil
|
targ := expand(targs[i]) // possibly nil
|
||||||
targs[i] = targ
|
targs[i] = targ
|
||||||
proj[tpar.typ.(*TypeParam)] = targ
|
proj[tpar.typ.(*_TypeParam)] = targ
|
||||||
}
|
}
|
||||||
return &substMap{targs, proj}
|
return &substMap{targs, proj}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ func (m *substMap) empty() bool {
|
||||||
return len(m.proj) == 0
|
return len(m.proj) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *substMap) lookup(tpar *TypeParam) Type {
|
func (m *substMap) lookup(tpar *_TypeParam) Type {
|
||||||
if t := m.proj[tpar]; t != nil {
|
if t := m.proj[tpar]; t != nil {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +121,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
||||||
|
|
||||||
// check bounds
|
// check bounds
|
||||||
for i, tname := range tparams {
|
for i, tname := range tparams {
|
||||||
tpar := tname.typ.(*TypeParam)
|
tpar := tname.typ.(*_TypeParam)
|
||||||
iface := tpar.Bound()
|
iface := tpar.Bound()
|
||||||
if iface.Empty() {
|
if iface.Empty() {
|
||||||
continue // no type bound
|
continue // no type bound
|
||||||
|
|
@ -222,7 +222,7 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
|
||||||
switch t := typ.(type) {
|
switch t := typ.(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
return typ // nothing to do
|
return typ // nothing to do
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
return smap.lookup(t)
|
return smap.lookup(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,13 +293,13 @@ func (subst *subster) typ(typ Type) Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
types, copied := subst.typeList(t.types)
|
types, copied := subst.typeList(t.types)
|
||||||
if copied {
|
if copied {
|
||||||
// Don't do it manually, with a Sum literal: the new
|
// Don't do it manually, with a Sum literal: the new
|
||||||
// types list may not be unique and NewSum may remove
|
// types list may not be unique and NewSum may remove
|
||||||
// duplicates.
|
// duplicates.
|
||||||
return NewSum(types)
|
return _NewSum(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
|
|
@ -389,7 +389,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||||
|
|
||||||
// create a new named type and populate caches to avoid endless recursion
|
// create a new named type and populate caches to avoid endless recursion
|
||||||
tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
|
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 := subst.check.newNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
|
||||||
named.tparams = t.tparams // new type is still parameterized
|
named.tparams = t.tparams // new type is still parameterized
|
||||||
named.targs = newTargs
|
named.targs = newTargs
|
||||||
subst.check.typMap[h] = named
|
subst.check.typMap[h] = named
|
||||||
|
|
@ -402,7 +402,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||||
|
|
||||||
return named
|
return named
|
||||||
|
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
return subst.smap.lookup(t)
|
return subst.smap.lookup(t)
|
||||||
|
|
||||||
case *instance:
|
case *instance:
|
||||||
|
|
|
||||||
|
|
@ -240,11 +240,11 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
|
||||||
// contain methods whose receiver type is a different interface.
|
// contain methods whose receiver type is a different interface.
|
||||||
func (s *Signature) Recv() *Var { return s.recv }
|
func (s *Signature) Recv() *Var { return s.recv }
|
||||||
|
|
||||||
// TParams returns the type parameters of signature s, or nil.
|
// _TParams returns the type parameters of signature s, or nil.
|
||||||
func (s *Signature) TParams() []*TypeName { return s.tparams }
|
func (s *Signature) _TParams() []*TypeName { return s.tparams }
|
||||||
|
|
||||||
// SetTParams sets the type parameters of signature s.
|
// _SetTParams sets the type parameters of signature s.
|
||||||
func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams }
|
func (s *Signature) _SetTParams(tparams []*TypeName) { s.tparams = tparams }
|
||||||
|
|
||||||
// Params returns the parameters of signature s, or nil.
|
// Params returns the parameters of signature s, or nil.
|
||||||
func (s *Signature) Params() *Tuple { return s.params }
|
func (s *Signature) Params() *Tuple { return s.params }
|
||||||
|
|
@ -255,19 +255,19 @@ func (s *Signature) Results() *Tuple { return s.results }
|
||||||
// Variadic reports whether the signature s is variadic.
|
// Variadic reports whether the signature s is variadic.
|
||||||
func (s *Signature) Variadic() bool { return s.variadic }
|
func (s *Signature) Variadic() bool { return s.variadic }
|
||||||
|
|
||||||
// A Sum represents a set of possible types.
|
// A _Sum represents a set of possible types.
|
||||||
// Sums are currently used to represent type lists of interfaces
|
// Sums are currently used to represent type lists of interfaces
|
||||||
// and thus the underlying types of type parameters; they are not
|
// and thus the underlying types of type parameters; they are not
|
||||||
// first class types of Go.
|
// first class types of Go.
|
||||||
type Sum struct {
|
type _Sum struct {
|
||||||
types []Type // types are unique
|
types []Type // types are unique
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSum returns a new Sum type consisting of the provided
|
// _NewSum returns a new Sum type consisting of the provided
|
||||||
// types if there are more than one. If there is exactly one
|
// types if there are more than one. If there is exactly one
|
||||||
// type, it returns that type. If the list of types is empty
|
// type, it returns that type. If the list of types is empty
|
||||||
// the result is nil.
|
// the result is nil.
|
||||||
func NewSum(types []Type) Type {
|
func _NewSum(types []Type) Type {
|
||||||
if len(types) == 0 {
|
if len(types) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -278,7 +278,7 @@ func NewSum(types []Type) Type {
|
||||||
// current use case of type lists.
|
// current use case of type lists.
|
||||||
// TODO(gri) Come up with the rules for sum types.
|
// TODO(gri) Come up with the rules for sum types.
|
||||||
for _, t := range types {
|
for _, t := range types {
|
||||||
if _, ok := t.(*Sum); ok {
|
if _, ok := t.(*_Sum); ok {
|
||||||
panic("sum type contains sum type - unimplemented")
|
panic("sum type contains sum type - unimplemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,11 +286,11 @@ func NewSum(types []Type) Type {
|
||||||
if len(types) == 1 {
|
if len(types) == 1 {
|
||||||
return types[0]
|
return types[0]
|
||||||
}
|
}
|
||||||
return &Sum{types: types}
|
return &_Sum{types: types}
|
||||||
}
|
}
|
||||||
|
|
||||||
// is reports whether all types in t satisfy pred.
|
// is reports whether all types in t satisfy pred.
|
||||||
func (s *Sum) is(pred func(Type) bool) bool {
|
func (s *_Sum) is(pred func(Type) bool) bool {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -446,8 +446,8 @@ func (t *Interface) Empty() bool {
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasTypeList reports whether interface t has a type list, possibly from an embedded type.
|
// _HasTypeList reports whether interface t has a type list, possibly from an embedded type.
|
||||||
func (t *Interface) HasTypeList() bool {
|
func (t *Interface) _HasTypeList() bool {
|
||||||
if t.allMethods != nil {
|
if t.allMethods != nil {
|
||||||
// interface is complete - quick test
|
// interface is complete - quick test
|
||||||
return t.allTypes != nil
|
return t.allTypes != nil
|
||||||
|
|
@ -458,8 +458,8 @@ func (t *Interface) HasTypeList() bool {
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
|
// _IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
|
||||||
func (t *Interface) IsComparable() bool {
|
func (t *Interface) _IsComparable() bool {
|
||||||
if t.allMethods != nil {
|
if t.allMethods != nil {
|
||||||
// interface is complete - quick test
|
// interface is complete - quick test
|
||||||
_, m := lookupMethod(t.allMethods, nil, "==")
|
_, m := lookupMethod(t.allMethods, nil, "==")
|
||||||
|
|
@ -472,8 +472,8 @@ func (t *Interface) IsComparable() bool {
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsConstraint reports t.HasTypeList() || t.IsComparable().
|
// _IsConstraint reports t.HasTypeList() || t.IsComparable().
|
||||||
func (t *Interface) IsConstraint() bool {
|
func (t *Interface) _IsConstraint() bool {
|
||||||
if t.allMethods != nil {
|
if t.allMethods != nil {
|
||||||
// interface is complete - quick test
|
// interface is complete - quick test
|
||||||
if t.allTypes != nil {
|
if t.allTypes != nil {
|
||||||
|
|
@ -667,7 +667,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods}
|
typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods}
|
||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
|
|
@ -681,15 +681,15 @@ func (t *Named) Obj() *TypeName { return t.obj }
|
||||||
// TODO(gri) Come up with a better representation and API to distinguish
|
// TODO(gri) Come up with a better representation and API to distinguish
|
||||||
// between parameterized instantiated and non-instantiated types.
|
// between parameterized instantiated and non-instantiated types.
|
||||||
|
|
||||||
// TParams returns the type parameters of the named type t, or nil.
|
// _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.
|
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
|
||||||
func (t *Named) TParams() []*TypeName { return t.tparams }
|
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.
|
// _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 }
|
func (t *Named) _TArgs() []Type { return t.targs }
|
||||||
|
|
||||||
// SetTArgs sets the type arguments of Named.
|
// _SetTArgs sets the type arguments of Named.
|
||||||
func (t *Named) SetTArgs(args []Type) { t.targs = args }
|
func (t *Named) _SetTArgs(args []Type) { t.targs = args }
|
||||||
|
|
||||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||||
func (t *Named) NumMethods() int { return len(t.methods) }
|
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||||
|
|
@ -715,8 +715,8 @@ func (t *Named) AddMethod(m *Func) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A TypeParam represents a type parameter type.
|
// A _TypeParam represents a type parameter type.
|
||||||
type TypeParam struct {
|
type _TypeParam struct {
|
||||||
check *Checker // for lazy type bound completion
|
check *Checker // for lazy type bound completion
|
||||||
id uint64 // unique id
|
id uint64 // unique id
|
||||||
obj *TypeName // corresponding type name
|
obj *TypeName // corresponding type name
|
||||||
|
|
@ -724,10 +724,10 @@ type TypeParam struct {
|
||||||
bound Type // *Named or *Interface; underlying type is always *Interface
|
bound Type // *Named or *Interface; underlying type is always *Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTypeParam returns a new TypeParam.
|
// newTypeParam returns a new TypeParam.
|
||||||
func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
|
func (check *Checker) newTypeParam(obj *TypeName, index int, bound Type) *_TypeParam {
|
||||||
assert(bound != nil)
|
assert(bound != nil)
|
||||||
typ := &TypeParam{check: check, id: check.nextId, obj: obj, index: index, bound: bound}
|
typ := &_TypeParam{check: check, id: check.nextId, obj: obj, index: index, bound: bound}
|
||||||
check.nextId++
|
check.nextId++
|
||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
|
|
@ -735,7 +735,7 @@ func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypePa
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TypeParam) Bound() *Interface {
|
func (t *_TypeParam) Bound() *Interface {
|
||||||
iface := asInterface(t.bound)
|
iface := asInterface(t.bound)
|
||||||
// use the type bound position if we have one
|
// use the type bound position if we have one
|
||||||
pos := token.NoPos
|
pos := token.NoPos
|
||||||
|
|
@ -846,12 +846,12 @@ func (t *Struct) Underlying() Type { return t }
|
||||||
func (t *Pointer) Underlying() Type { return t }
|
func (t *Pointer) Underlying() Type { return t }
|
||||||
func (t *Tuple) Underlying() Type { return t }
|
func (t *Tuple) Underlying() Type { return t }
|
||||||
func (t *Signature) Underlying() Type { return t }
|
func (t *Signature) Underlying() Type { return t }
|
||||||
func (t *Sum) Underlying() Type { return t }
|
func (t *_Sum) Underlying() Type { return t }
|
||||||
func (t *Interface) Underlying() Type { return t }
|
func (t *Interface) Underlying() Type { return t }
|
||||||
func (t *Map) Underlying() Type { return t }
|
func (t *Map) Underlying() Type { return t }
|
||||||
func (t *Chan) Underlying() Type { return t }
|
func (t *Chan) Underlying() Type { return t }
|
||||||
func (t *Named) Underlying() Type { return t.underlying }
|
func (t *Named) Underlying() Type { return t.underlying }
|
||||||
func (t *TypeParam) Underlying() Type { return t }
|
func (t *_TypeParam) Underlying() Type { return t }
|
||||||
func (t *instance) Underlying() Type { return t }
|
func (t *instance) Underlying() Type { return t }
|
||||||
func (t *bottom) Underlying() Type { return t }
|
func (t *bottom) Underlying() Type { return t }
|
||||||
func (t *top) Underlying() Type { return t }
|
func (t *top) Underlying() Type { return t }
|
||||||
|
|
@ -864,12 +864,12 @@ func (t *Struct) String() string { return TypeString(t, nil) }
|
||||||
func (t *Pointer) 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 *Tuple) String() string { return TypeString(t, nil) }
|
||||||
func (t *Signature) 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 *_Sum) String() string { return TypeString(t, nil) }
|
||||||
func (t *Interface) 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 *Map) String() string { return TypeString(t, nil) }
|
||||||
func (t *Chan) 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 *Named) String() string { return TypeString(t, nil) }
|
||||||
func (t *TypeParam) 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 *instance) String() string { return TypeString(t, nil) }
|
||||||
func (t *bottom) String() string { return TypeString(t, nil) }
|
func (t *bottom) String() string { return TypeString(t, nil) }
|
||||||
func (t *top) String() string { return TypeString(t, nil) }
|
func (t *top) String() string { return TypeString(t, nil) }
|
||||||
|
|
@ -909,22 +909,11 @@ func asSlice(t Type) *Slice {
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (rFindley) delete this on the dev.typeparams branch. This is only
|
|
||||||
// exported in the prototype for legacy compatibility.
|
|
||||||
func AsStruct(t Type) *Struct {
|
|
||||||
return asStruct(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func asStruct(t Type) *Struct {
|
func asStruct(t Type) *Struct {
|
||||||
op, _ := optype(t).(*Struct)
|
op, _ := optype(t).(*Struct)
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rFindley) delete this on the dev.typeparams branch (see ToStruct).
|
|
||||||
func AsPointer(t Type) *Pointer {
|
|
||||||
return asPointer(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func asPointer(t Type) *Pointer {
|
func asPointer(t Type) *Pointer {
|
||||||
op, _ := optype(t).(*Pointer)
|
op, _ := optype(t).(*Pointer)
|
||||||
return op
|
return op
|
||||||
|
|
@ -940,8 +929,8 @@ func asSignature(t Type) *Signature {
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func asSum(t Type) *Sum {
|
func asSum(t Type) *_Sum {
|
||||||
op, _ := optype(t).(*Sum)
|
op, _ := optype(t).(*_Sum)
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -969,7 +958,7 @@ func asNamed(t Type) *Named {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func asTypeParam(t Type) *TypeParam {
|
func asTypeParam(t Type) *_TypeParam {
|
||||||
u, _ := under(t).(*TypeParam)
|
u, _ := under(t).(*_TypeParam)
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||||
buf.WriteString("func")
|
buf.WriteString("func")
|
||||||
writeSignature(buf, t, qf, visited)
|
writeSignature(buf, t, qf, visited)
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
for i, t := range t.types {
|
for i, t := range t.types {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.WriteString(", ")
|
buf.WriteString(", ")
|
||||||
|
|
@ -279,7 +279,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||||
writeTParamList(buf, t.tparams, qf, visited)
|
writeTParamList(buf, t.tparams, qf, visited)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *TypeParam:
|
case *_TypeParam:
|
||||||
s := "?"
|
s := "?"
|
||||||
if t.obj != nil {
|
if t.obj != nil {
|
||||||
s = t.obj.name
|
s = t.obj.name
|
||||||
|
|
@ -321,7 +321,7 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited
|
||||||
for i, p := range list {
|
for i, p := range list {
|
||||||
// TODO(rFindley) support 'any' sugar here.
|
// TODO(rFindley) support 'any' sugar here.
|
||||||
var b Type = &emptyInterface
|
var b Type = &emptyInterface
|
||||||
if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
|
if t, _ := p.typ.(*_TypeParam); t != nil && t.bound != nil {
|
||||||
b = t.bound
|
b = t.bound
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
|
@ -334,7 +334,7 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited
|
||||||
}
|
}
|
||||||
prev = b
|
prev = b
|
||||||
|
|
||||||
if t, _ := p.typ.(*TypeParam); t != nil {
|
if t, _ := p.typ.(*_TypeParam); t != nil {
|
||||||
writeType(buf, t, qf, visited)
|
writeType(buf, t, qf, visited)
|
||||||
} else {
|
} else {
|
||||||
buf.WriteString(p.name)
|
buf.WriteString(p.name)
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func (check *Checker) ordinaryType(pos positioner, typ Type) {
|
||||||
check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes)
|
check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.IsComparable() {
|
if t._IsComparable() {
|
||||||
check.softErrorf(pos, _Todo, "interface is (or embeds) comparable")
|
check.softErrorf(pos, _Todo, "interface is (or embeds) comparable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +301,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
||||||
}
|
}
|
||||||
smap := makeSubstMap(recvTParams, list)
|
smap := makeSubstMap(recvTParams, list)
|
||||||
for i, tname := range sig.rparams {
|
for i, tname := range sig.rparams {
|
||||||
bound := recvTParams[i].typ.(*TypeParam).bound
|
bound := recvTParams[i].typ.(*_TypeParam).bound
|
||||||
// bound is (possibly) parameterized in the context of the
|
// bound is (possibly) parameterized in the context of the
|
||||||
// receiver type declaration. Substitute parameters for the
|
// receiver type declaration. Substitute parameters for the
|
||||||
// current context.
|
// current context.
|
||||||
|
|
@ -309,7 +309,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
||||||
// (no bound == empty interface)
|
// (no bound == empty interface)
|
||||||
if bound != nil {
|
if bound != nil {
|
||||||
bound = check.subst(tname.pos, bound, smap)
|
bound = check.subst(tname.pos, bound, smap)
|
||||||
tname.typ.(*TypeParam).bound = bound
|
tname.typ.(*_TypeParam).bound = bound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -333,7 +333,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
||||||
recvList, _ := check.collectParams(scope, recvPar, recvTyp, false) // use rewritten receiver type, if any
|
recvList, _ := check.collectParams(scope, recvPar, recvTyp, false) // use rewritten receiver type, if any
|
||||||
params, variadic := check.collectParams(scope, ftyp.Params, nil, true)
|
params, variadic := check.collectParams(scope, ftyp.Params, nil, true)
|
||||||
results, _ := check.collectParams(scope, ftyp.Results, nil, false)
|
results, _ := check.collectParams(scope, ftyp.Results, nil, false)
|
||||||
scope.Squash(func(obj, alt Object) {
|
scope.squash(func(obj, alt Object) {
|
||||||
check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
|
check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
})
|
})
|
||||||
|
|
@ -823,7 +823,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// type constraints
|
// type constraints
|
||||||
ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types))
|
ityp.types = _NewSum(check.collectTypeConstraints(iface.Pos(), types))
|
||||||
|
|
||||||
if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
|
if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
|
||||||
// empty interface
|
// empty interface
|
||||||
|
|
@ -928,7 +928,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
|
||||||
if etyp == nil {
|
if etyp == nil {
|
||||||
if utyp != Typ[Invalid] {
|
if utyp != Typ[Invalid] {
|
||||||
var format string
|
var format string
|
||||||
if _, ok := utyp.(*TypeParam); ok {
|
if _, ok := utyp.(*_TypeParam); ok {
|
||||||
format = "%s is a type parameter, not an interface"
|
format = "%s is a type parameter, not an interface"
|
||||||
} else {
|
} else {
|
||||||
format = "%s is not an interface"
|
format = "%s is not an interface"
|
||||||
|
|
@ -987,7 +987,7 @@ func intersect(x, y Type) (r Type) {
|
||||||
if rtypes == nil {
|
if rtypes == nil {
|
||||||
return theBottom
|
return theBottom
|
||||||
}
|
}
|
||||||
return NewSum(rtypes)
|
return _NewSum(rtypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortTypes(list []Type) {
|
func sortTypes(list []Type) {
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func (d *tparamsList) init(tparams []*TypeName) {
|
||||||
}
|
}
|
||||||
if debug {
|
if debug {
|
||||||
for i, tpar := range tparams {
|
for i, tpar := range tparams {
|
||||||
assert(i == tpar.typ.(*TypeParam).index)
|
assert(i == tpar.typ.(*_TypeParam).index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.tparams = tparams
|
d.tparams = tparams
|
||||||
|
|
@ -131,7 +131,7 @@ func (u *unifier) join(i, j int) bool {
|
||||||
// If typ is a type parameter of d, index returns the type parameter index.
|
// If typ is a type parameter of d, index returns the type parameter index.
|
||||||
// Otherwise, the result is < 0.
|
// Otherwise, the result is < 0.
|
||||||
func (d *tparamsList) index(typ Type) int {
|
func (d *tparamsList) index(typ Type) int {
|
||||||
if t, ok := typ.(*TypeParam); ok {
|
if t, ok := typ.(*_TypeParam); ok {
|
||||||
if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
|
if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
@ -335,7 +335,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
|
||||||
u.nify(x.results, y.results, p)
|
u.nify(x.results, y.results, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Sum:
|
case *_Sum:
|
||||||
// This should not happen with the current internal use of sum types.
|
// This should not happen with the current internal use of sum types.
|
||||||
panic("type inference across sum types not implemented")
|
panic("type inference across sum types not implemented")
|
||||||
|
|
||||||
|
|
@ -431,7 +431,7 @@ 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
|
// 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)
|
// enclosing type as those are handled in the beginning of this function)
|
||||||
// are identical if they originate in the same declaration.
|
// are identical if they originate in the same declaration.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue