mirror of
https://github.com/golang/go
synced 2024-11-20 05:04:43 -07:00
exp/types: completed typechecking of parameter passing
Details: - fixed variadic parameter passing and calls of the form f(g()) - fixed implementation of ^x for unsigned constants x - fixed assignability of untyped booleans - resolved a few TODOs, various minor fixes - enabled many more tests (only 6 std packages don't typecheck) R=rsc CC=golang-dev https://golang.org/cl/6930053
This commit is contained in:
parent
b7603cfc2c
commit
50d8787822
@ -50,7 +50,7 @@ var tests = []string{
|
|||||||
|
|
||||||
// directories
|
// directories
|
||||||
// Note: packages that don't typecheck yet are commented out
|
// Note: packages that don't typecheck yet are commented out
|
||||||
// "archive/tar", // investigate
|
"archive/tar",
|
||||||
"archive/zip",
|
"archive/zip",
|
||||||
|
|
||||||
"bufio",
|
"bufio",
|
||||||
@ -77,13 +77,13 @@ var tests = []string{
|
|||||||
"crypto/md5",
|
"crypto/md5",
|
||||||
"crypto/rand",
|
"crypto/rand",
|
||||||
"crypto/rc4",
|
"crypto/rc4",
|
||||||
// "crypto/rsa", // investigate (GOARCH=386)
|
// "crypto/rsa", // src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io
|
||||||
"crypto/sha1",
|
"crypto/sha1",
|
||||||
"crypto/sha256",
|
"crypto/sha256",
|
||||||
"crypto/sha512",
|
"crypto/sha512",
|
||||||
"crypto/subtle",
|
"crypto/subtle",
|
||||||
"crypto/tls",
|
"crypto/tls",
|
||||||
// "crypto/x509", // investigate
|
// "crypto/x509", // src/pkg/crypto/x509/root.go:15:10: undeclared name: initSystemRoots
|
||||||
"crypto/x509/pkix",
|
"crypto/x509/pkix",
|
||||||
|
|
||||||
"database/sql",
|
"database/sql",
|
||||||
@ -117,7 +117,7 @@ var tests = []string{
|
|||||||
|
|
||||||
"go/ast",
|
"go/ast",
|
||||||
"go/build",
|
"go/build",
|
||||||
// "go/doc", // variadic parameters don't work yet fully
|
"go/doc",
|
||||||
"go/format",
|
"go/format",
|
||||||
"go/parser",
|
"go/parser",
|
||||||
"go/printer",
|
"go/printer",
|
||||||
@ -125,7 +125,7 @@ var tests = []string{
|
|||||||
"go/token",
|
"go/token",
|
||||||
|
|
||||||
"hash/adler32",
|
"hash/adler32",
|
||||||
// "hash/crc32", // investigate
|
"hash/crc32",
|
||||||
"hash/crc64",
|
"hash/crc64",
|
||||||
"hash/fnv",
|
"hash/fnv",
|
||||||
|
|
||||||
@ -139,54 +139,54 @@ var tests = []string{
|
|||||||
"index/suffixarray",
|
"index/suffixarray",
|
||||||
|
|
||||||
"io",
|
"io",
|
||||||
// "io/ioutil", // investigate
|
"io/ioutil",
|
||||||
|
|
||||||
"log",
|
"log",
|
||||||
"log/syslog",
|
"log/syslog",
|
||||||
|
|
||||||
"math",
|
"math",
|
||||||
// "math/big", // investigate
|
"math/big",
|
||||||
"math/cmplx",
|
"math/cmplx",
|
||||||
"math/rand",
|
"math/rand",
|
||||||
|
|
||||||
"mime",
|
"mime",
|
||||||
"mime/multipart",
|
"mime/multipart",
|
||||||
|
|
||||||
// "net", // depends on C files
|
// "net", // src/pkg/net/lookup_unix.go:56:20: undeclared name: cgoLookupHost
|
||||||
"net/http",
|
"net/http",
|
||||||
"net/http/cgi",
|
"net/http/cgi",
|
||||||
// "net/http/fcgi", // investigate
|
"net/http/fcgi",
|
||||||
"net/http/httptest",
|
"net/http/httptest",
|
||||||
"net/http/httputil",
|
"net/http/httputil",
|
||||||
// "net/http/pprof", // investigate
|
"net/http/pprof",
|
||||||
"net/mail",
|
"net/mail",
|
||||||
// "net/rpc", // investigate
|
"net/rpc",
|
||||||
"net/rpc/jsonrpc",
|
"net/rpc/jsonrpc",
|
||||||
"net/smtp",
|
"net/smtp",
|
||||||
"net/textproto",
|
"net/textproto",
|
||||||
"net/url",
|
"net/url",
|
||||||
|
|
||||||
// "path", // variadic parameters don't work yet fully
|
"path",
|
||||||
// "path/filepath", // investigate
|
"path/filepath",
|
||||||
|
|
||||||
// "reflect", // investigate
|
// "reflect", // unsafe.Sizeof must return size > 0 for pointer types
|
||||||
|
|
||||||
"regexp",
|
"regexp",
|
||||||
"regexp/syntax",
|
"regexp/syntax",
|
||||||
|
|
||||||
"runtime",
|
"runtime",
|
||||||
// "runtime/cgo", // import "C"
|
"runtime/cgo",
|
||||||
"runtime/debug",
|
"runtime/debug",
|
||||||
"runtime/pprof",
|
"runtime/pprof",
|
||||||
|
|
||||||
"sort",
|
"sort",
|
||||||
// "strconv", // investigate
|
// "strconv", // bug in switch case duplicate detection
|
||||||
"strings",
|
"strings",
|
||||||
|
|
||||||
// "sync", // platform-specific files
|
"sync",
|
||||||
// "sync/atomic", // platform-specific files
|
"sync/atomic",
|
||||||
|
|
||||||
// "syscall", // platform-specific files
|
"syscall",
|
||||||
|
|
||||||
"testing",
|
"testing",
|
||||||
"testing/iotest",
|
"testing/iotest",
|
||||||
@ -194,10 +194,10 @@ var tests = []string{
|
|||||||
|
|
||||||
"text/scanner",
|
"text/scanner",
|
||||||
"text/tabwriter",
|
"text/tabwriter",
|
||||||
// "text/template", // variadic parameters don't work yet fully
|
"text/template",
|
||||||
// "text/template/parse", // variadic parameters don't work yet fully
|
"text/template/parse",
|
||||||
|
|
||||||
// "time", // platform-specific files
|
// "time", // local const decls without initialization expressions
|
||||||
"unicode",
|
"unicode",
|
||||||
"unicode/utf16",
|
"unicode/utf16",
|
||||||
"unicode/utf8",
|
"unicode/utf8",
|
||||||
|
@ -155,13 +155,14 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
|
|||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typ := underlying(x.typ).(*Basic)
|
||||||
if x.mode == constant && y.mode == constant {
|
if x.mode == constant && y.mode == constant {
|
||||||
x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, false)
|
x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ)
|
||||||
} else {
|
} else {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
}
|
}
|
||||||
|
|
||||||
switch underlying(x.typ).(*Basic).Kind {
|
switch typ.Kind {
|
||||||
case Float32:
|
case Float32:
|
||||||
x.typ = Typ[Complex64]
|
x.typ = Typ[Complex64]
|
||||||
case Float64:
|
case Float64:
|
||||||
|
@ -49,12 +49,17 @@ func (nilType) String() string {
|
|||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frequently used constants.
|
// Implementation-specific constants.
|
||||||
|
// TODO(gri) These need to go elsewhere.
|
||||||
|
const (
|
||||||
|
intBits = 32
|
||||||
|
ptrBits = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// Frequently used values.
|
||||||
var (
|
var (
|
||||||
zeroConst = int64(0)
|
nilConst = nilType{}
|
||||||
oneConst = int64(1)
|
zeroConst = int64(0)
|
||||||
minusOneConst = int64(-1)
|
|
||||||
nilConst = nilType{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// int64 bounds
|
// int64 bounds
|
||||||
@ -74,7 +79,7 @@ func normalizeIntConst(x *big.Int) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normalizeRatConst returns the smallest constant representation
|
// normalizeRatConst returns the smallest constant representation
|
||||||
// for the specific value of x; either an int64, *big.Int value,
|
// for the specific value of x; either an int64, *big.Int,
|
||||||
// or *big.Rat value.
|
// or *big.Rat value.
|
||||||
//
|
//
|
||||||
func normalizeRatConst(x *big.Rat) interface{} {
|
func normalizeRatConst(x *big.Rat) interface{} {
|
||||||
@ -84,15 +89,15 @@ func normalizeRatConst(x *big.Rat) interface{} {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeComplexConst returns the smallest constant representation
|
// newComplex returns the smallest constant representation
|
||||||
// for the specific value of x; either an int64, *big.Int value, *big.Rat,
|
// for the specific value re + im*i; either an int64, *big.Int,
|
||||||
// or complex value.
|
// *big.Rat, or complex value.
|
||||||
//
|
//
|
||||||
func normalizeComplexConst(x complex) interface{} {
|
func newComplex(re, im *big.Rat) interface{} {
|
||||||
if x.im.Sign() == 0 {
|
if im.Sign() == 0 {
|
||||||
return normalizeRatConst(x.re)
|
return normalizeRatConst(re)
|
||||||
}
|
}
|
||||||
return x
|
return complex{re, im}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeRuneConst returns the int64 code point for the rune literal
|
// makeRuneConst returns the int64 code point for the rune literal
|
||||||
@ -140,7 +145,7 @@ func makeComplexConst(lit string) interface{} {
|
|||||||
n := len(lit)
|
n := len(lit)
|
||||||
if n > 0 && lit[n-1] == 'i' {
|
if n > 0 && lit[n-1] == 'i' {
|
||||||
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
|
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
|
||||||
return normalizeComplexConst(complex{big.NewRat(0, 1), im})
|
return newComplex(big.NewRat(0, 1), im)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -202,9 +207,6 @@ func isNegConst(x interface{}) bool {
|
|||||||
// of precision.
|
// of precision.
|
||||||
//
|
//
|
||||||
func isRepresentableConst(x interface{}, as BasicKind) bool {
|
func isRepresentableConst(x interface{}, as BasicKind) bool {
|
||||||
const intBits = 32 // TODO(gri) implementation-specific constant
|
|
||||||
const ptrBits = 64 // TODO(gri) implementation-specific constant
|
|
||||||
|
|
||||||
switch x := x.(type) {
|
switch x := x.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
return as == Bool || as == UntypedBool
|
return as == Bool || as == UntypedBool
|
||||||
@ -386,13 +388,71 @@ func is63bit(x int64) bool {
|
|||||||
return -1<<62 <= x && x <= 1<<62-1
|
return -1<<62 <= x && x <= 1<<62-1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
|
||||||
|
func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} {
|
||||||
|
switch op {
|
||||||
|
case token.ADD:
|
||||||
|
return x // nothing to do
|
||||||
|
case token.SUB:
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64:
|
||||||
|
if z := -x; z != x {
|
||||||
|
return z // no overflow
|
||||||
|
}
|
||||||
|
// overflow - need to convert to big.Int
|
||||||
|
return normalizeIntConst(new(big.Int).Neg(big.NewInt(x)))
|
||||||
|
case *big.Int:
|
||||||
|
return normalizeIntConst(new(big.Int).Neg(x))
|
||||||
|
case *big.Rat:
|
||||||
|
return normalizeRatConst(new(big.Rat).Neg(x))
|
||||||
|
case complex:
|
||||||
|
return newComplex(new(big.Rat).Neg(x.re), new(big.Rat).Neg(x.im))
|
||||||
|
}
|
||||||
|
case token.XOR:
|
||||||
|
var z big.Int
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64:
|
||||||
|
z.Not(big.NewInt(x))
|
||||||
|
case *big.Int:
|
||||||
|
z.Not(x)
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
// For unsigned types, the result will be negative and
|
||||||
|
// thus "too large": We must limit the result size to
|
||||||
|
// the type's size.
|
||||||
|
if typ.Info&IsUnsigned != 0 {
|
||||||
|
s := uint(typ.Size) * 8
|
||||||
|
if s == 0 {
|
||||||
|
// platform-specific type
|
||||||
|
// TODO(gri) this needs to be factored out
|
||||||
|
switch typ.Kind {
|
||||||
|
case Uint:
|
||||||
|
s = intBits
|
||||||
|
case Uintptr:
|
||||||
|
s = ptrBits
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// z &^= (-1)<<s
|
||||||
|
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s))
|
||||||
|
}
|
||||||
|
return normalizeIntConst(&z)
|
||||||
|
case token.NOT:
|
||||||
|
return !x.(bool)
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// binaryOpConst returns the result of the constant evaluation x op y;
|
// binaryOpConst returns the result of the constant evaluation x op y;
|
||||||
// both operands must be of the same "kind" (boolean, numeric, or string).
|
// both operands must be of the same constant "kind" (boolean, numeric, or string).
|
||||||
// If intDiv is true, division (op == token.QUO) is using integer division
|
// If typ is an integer type, division (op == token.QUO) is using integer division
|
||||||
// (and the result is guaranteed to be integer) rather than floating-point
|
// (and the result is guaranteed to be integer) rather than floating-point
|
||||||
// division. Division by zero leads to a run-time panic.
|
// division. Division by zero leads to a run-time panic.
|
||||||
//
|
//
|
||||||
func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
|
||||||
x, y = matchConst(x, y)
|
x, y = matchConst(x, y)
|
||||||
|
|
||||||
switch x := x.(type) {
|
switch x := x.(type) {
|
||||||
@ -403,8 +463,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
|||||||
return x && y
|
return x && y
|
||||||
case token.LOR:
|
case token.LOR:
|
||||||
return x || y
|
return x || y
|
||||||
default:
|
|
||||||
unreachable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case int64:
|
case int64:
|
||||||
@ -431,7 +489,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
|||||||
case token.REM:
|
case token.REM:
|
||||||
return x % y
|
return x % y
|
||||||
case token.QUO:
|
case token.QUO:
|
||||||
if intDiv {
|
if typ.Info&IsInteger != 0 {
|
||||||
return x / y
|
return x / y
|
||||||
}
|
}
|
||||||
return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
|
return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
|
||||||
@ -443,8 +501,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
|||||||
return x ^ y
|
return x ^ y
|
||||||
case token.AND_NOT:
|
case token.AND_NOT:
|
||||||
return x &^ y
|
return x &^ y
|
||||||
default:
|
|
||||||
unreachable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *big.Int:
|
case *big.Int:
|
||||||
@ -460,7 +516,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
|||||||
case token.REM:
|
case token.REM:
|
||||||
z.Rem(x, y)
|
z.Rem(x, y)
|
||||||
case token.QUO:
|
case token.QUO:
|
||||||
if intDiv {
|
if typ.Info&IsInteger != 0 {
|
||||||
z.Quo(x, y)
|
z.Quo(x, y)
|
||||||
} else {
|
} else {
|
||||||
return normalizeRatConst(new(big.Rat).SetFrac(x, y))
|
return normalizeRatConst(new(big.Rat).SetFrac(x, y))
|
||||||
@ -533,7 +589,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
|
|||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
return normalizeComplexConst(complex{&re, &im})
|
return newComplex(&re, &im)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if op == token.ADD {
|
if op == token.ADD {
|
||||||
|
@ -266,15 +266,8 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
|||||||
buf.WriteByte('*')
|
buf.WriteByte('*')
|
||||||
writeType(buf, t.Base)
|
writeType(buf, t.Base)
|
||||||
|
|
||||||
case *tuple:
|
case *Result:
|
||||||
buf.WriteByte('(')
|
writeParams(buf, t.Values, false)
|
||||||
for i, typ := range t.list {
|
|
||||||
if i > 0 {
|
|
||||||
buf.WriteString("; ")
|
|
||||||
}
|
|
||||||
writeType(buf, typ)
|
|
||||||
}
|
|
||||||
buf.WriteByte(')')
|
|
||||||
|
|
||||||
case *Signature:
|
case *Signature:
|
||||||
buf.WriteString("func")
|
buf.WriteString("func")
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
|
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
|
||||||
|
|
||||||
// TODO(gri) API issues
|
// TODO(gri) API issues
|
||||||
// - clients need access to result type information (tuples)
|
|
||||||
// - clients need access to constant values
|
// - clients need access to constant values
|
||||||
// - clients need access to built-in type information
|
// - clients need access to built-in type information
|
||||||
|
|
||||||
@ -212,21 +211,11 @@ func (check *checker) unary(x *operand, op token.Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
switch op {
|
typ := underlying(x.typ).(*Basic)
|
||||||
case token.ADD:
|
x.val = unaryOpConst(x.val, op, typ)
|
||||||
// nothing to do
|
|
||||||
case token.SUB:
|
|
||||||
x.val = binaryOpConst(zeroConst, x.val, token.SUB, false)
|
|
||||||
case token.XOR:
|
|
||||||
x.val = binaryOpConst(minusOneConst, x.val, token.XOR, false)
|
|
||||||
case token.NOT:
|
|
||||||
x.val = !x.val.(bool)
|
|
||||||
default:
|
|
||||||
unreachable() // operators where checked by check.op
|
|
||||||
}
|
|
||||||
// Typed constants must be representable in
|
// Typed constants must be representable in
|
||||||
// their type after each constant operation.
|
// their type after each constant operation.
|
||||||
check.isRepresentable(x, underlying(x.typ).(*Basic))
|
check.isRepresentable(x, typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +293,8 @@ func (check *checker) convertUntyped(x *operand, target Type) {
|
|||||||
if !x.isNil() {
|
if !x.isNil() {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
x.typ = target
|
x.typ = target
|
||||||
@ -332,7 +323,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !valid {
|
if !valid {
|
||||||
check.invalidOp(x.pos(), "cannot compare %s and %s", x, y)
|
check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -465,10 +456,11 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if x.mode == constant && y.mode == constant {
|
if x.mode == constant && y.mode == constant {
|
||||||
x.val = binaryOpConst(x.val, y.val, op, isInteger(x.typ))
|
typ := underlying(x.typ).(*Basic)
|
||||||
|
x.val = binaryOpConst(x.val, y.val, op, typ)
|
||||||
// Typed constants must be representable in
|
// Typed constants must be representable in
|
||||||
// their type after each constant operation.
|
// their type after each constant operation.
|
||||||
check.isRepresentable(x, underlying(x.typ).(*Basic))
|
check.isRepresentable(x, typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,9 +546,15 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
|
|||||||
return max
|
return max
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
|
// argument typechecks passing an argument arg (if arg != nil) or
|
||||||
|
// x (if arg == nil) to the i'th parameter of the given signature.
|
||||||
|
// If passSlice is set, the argument is followed by ... in the call.
|
||||||
|
//
|
||||||
|
func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
|
||||||
|
// determine parameter
|
||||||
var par *ast.Object
|
var par *ast.Object
|
||||||
if n := len(sig.Params); i < n {
|
n := len(sig.Params)
|
||||||
|
if i < n {
|
||||||
par = sig.Params[i]
|
par = sig.Params[i]
|
||||||
} else if sig.IsVariadic {
|
} else if sig.IsVariadic {
|
||||||
par = sig.Params[n-1]
|
par = sig.Params[n-1]
|
||||||
@ -565,16 +563,32 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) deal with ... last argument
|
// determine argument
|
||||||
var z, x operand
|
var z operand
|
||||||
z.mode = variable
|
z.mode = variable
|
||||||
z.expr = nil // TODO(gri) can we do better here?
|
z.expr = nil // TODO(gri) can we do better here? (for good error messages)
|
||||||
z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
|
z.typ = par.Type.(Type)
|
||||||
check.expr(&x, arg, z.typ, -1)
|
|
||||||
|
if arg != nil {
|
||||||
|
check.expr(x, arg, z.typ, -1)
|
||||||
|
}
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return // ignore this argument
|
return // ignore this argument
|
||||||
}
|
}
|
||||||
check.assignOperand(&z, &x)
|
|
||||||
|
// check last argument of the form x...
|
||||||
|
if passSlice {
|
||||||
|
if i+1 != n {
|
||||||
|
check.errorf(x.pos(), "can only use ... with matching parameter")
|
||||||
|
return // ignore this argument
|
||||||
|
}
|
||||||
|
// spec: "If the final argument is assignable to a slice type []T,
|
||||||
|
// it may be passed unchanged as the value for a ...T parameter if
|
||||||
|
// the argument is followed by ..."
|
||||||
|
z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignOperand(&z, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) recordType(x *operand) {
|
func (check *checker) recordType(x *operand) {
|
||||||
@ -1052,25 +1066,79 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||||||
check.conversion(x, e, x.typ, iota)
|
check.conversion(x, e, x.typ, iota)
|
||||||
} else if sig, ok := underlying(x.typ).(*Signature); ok {
|
} else if sig, ok := underlying(x.typ).(*Signature); ok {
|
||||||
// check parameters
|
// check parameters
|
||||||
// TODO(gri)
|
|
||||||
// - deal with single multi-valued function arguments: f(g())
|
// If we have a trailing ... at the end of the parameter
|
||||||
// - variadic functions only partially addressed
|
// list, the last argument must match the parameter type
|
||||||
for i, arg := range e.Args {
|
// []T of a variadic function parameter x ...T.
|
||||||
check.argument(sig, i, arg)
|
passSlice := false
|
||||||
|
if e.Ellipsis.IsValid() {
|
||||||
|
if sig.IsVariadic {
|
||||||
|
passSlice = true
|
||||||
|
} else {
|
||||||
|
check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a single argument that is a function call
|
||||||
|
// we need to handle it separately. Determine if this
|
||||||
|
// is the case without checking the argument.
|
||||||
|
var call *ast.CallExpr
|
||||||
|
if len(e.Args) == 1 {
|
||||||
|
call, _ = unparen(e.Args[0]).(*ast.CallExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0 // parameter count
|
||||||
|
if call != nil {
|
||||||
|
// We have a single argument that is a function call.
|
||||||
|
check.expr(x, call, nil, -1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error // TODO(gri): we can do better
|
||||||
|
}
|
||||||
|
if t, _ := x.typ.(*Result); t != nil {
|
||||||
|
// multiple result values
|
||||||
|
n = len(t.Values)
|
||||||
|
for i, obj := range t.Values {
|
||||||
|
x.mode = value
|
||||||
|
x.expr = nil // TODO(gri) can we do better here? (for good error messages)
|
||||||
|
x.typ = obj.Type.(Type)
|
||||||
|
check.argument(sig, i, nil, x, passSlice && i+1 == n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// single result value
|
||||||
|
n = 1
|
||||||
|
check.argument(sig, 0, nil, x, passSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// We don't have a single argument or it is not a function call.
|
||||||
|
n = len(e.Args)
|
||||||
|
for i, arg := range e.Args {
|
||||||
|
check.argument(sig, i, arg, x, passSlice && i+1 == n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if we have enough arguments
|
||||||
|
if sig.IsVariadic {
|
||||||
|
// a variadic function accepts an "empty"
|
||||||
|
// last argument: count one extra
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if n < len(sig.Params) {
|
||||||
|
check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
|
||||||
|
// ok to continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine result
|
// determine result
|
||||||
x.mode = value
|
switch len(sig.Results) {
|
||||||
if len(sig.Results) == 1 {
|
case 0:
|
||||||
|
x.mode = novalue
|
||||||
|
case 1:
|
||||||
|
x.mode = value
|
||||||
x.typ = sig.Results[0].Type.(Type)
|
x.typ = sig.Results[0].Type.(Type)
|
||||||
} else {
|
default:
|
||||||
// TODO(gri) change Signature representation to use tuples,
|
x.mode = value
|
||||||
// then this conversion is not required
|
x.typ = &Result{Values: sig.Results}
|
||||||
list := make([]Type, len(sig.Results))
|
|
||||||
for i, obj := range sig.Results {
|
|
||||||
list[i] = obj.Type.(Type)
|
|
||||||
}
|
|
||||||
x.typ = &tuple{list: list}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if bin, ok := x.typ.(*builtin); ok {
|
} else if bin, ok := x.typ.(*builtin); ok {
|
||||||
|
@ -182,7 +182,14 @@ func (x *operand) isAssignable(T Type) bool {
|
|||||||
if isUntyped(Vu) {
|
if isUntyped(Vu) {
|
||||||
switch t := Tu.(type) {
|
switch t := Tu.(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
return x.mode == constant && isRepresentableConst(x.val, t.Kind)
|
if x.mode == constant {
|
||||||
|
return isRepresentableConst(x.val, t.Kind)
|
||||||
|
}
|
||||||
|
// The result of a comparison is an untyped boolean,
|
||||||
|
// but may not be a constant.
|
||||||
|
if Vb, _ := Vu.(*Basic); Vb != nil {
|
||||||
|
return Vb.Kind == UntypedBool && isBoolean(Tu)
|
||||||
|
}
|
||||||
case *Interface:
|
case *Interface:
|
||||||
return x.isNil() || len(t.Methods) == 0
|
return x.isNil() || len(t.Methods) == 0
|
||||||
case *Pointer, *Signature, *Slice, *Map, *Chan:
|
case *Pointer, *Signature, *Slice, *Map, *Chan:
|
||||||
|
@ -225,25 +225,28 @@ func deref(typ Type) Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// defaultType returns the default "typed" type for an "untyped" type;
|
// defaultType returns the default "typed" type for an "untyped" type;
|
||||||
// it returns the argument typ for all other types.
|
// it returns the incoming type for all other types. If there is no
|
||||||
|
// corresponding untyped type, the result is Typ[Invalid].
|
||||||
|
//
|
||||||
func defaultType(typ Type) Type {
|
func defaultType(typ Type) Type {
|
||||||
if t, ok := typ.(*Basic); ok {
|
if t, ok := typ.(*Basic); ok {
|
||||||
var k BasicKind
|
k := Invalid
|
||||||
switch t.Kind {
|
switch t.Kind {
|
||||||
|
// case UntypedNil:
|
||||||
|
// There is no default type for nil. For a good error message,
|
||||||
|
// catch this case before calling this function.
|
||||||
case UntypedBool:
|
case UntypedBool:
|
||||||
k = Bool
|
k = Bool
|
||||||
case UntypedRune:
|
|
||||||
k = Rune
|
|
||||||
case UntypedInt:
|
case UntypedInt:
|
||||||
k = Int
|
k = Int
|
||||||
|
case UntypedRune:
|
||||||
|
k = Rune
|
||||||
case UntypedFloat:
|
case UntypedFloat:
|
||||||
k = Float64
|
k = Float64
|
||||||
case UntypedComplex:
|
case UntypedComplex:
|
||||||
k = Complex128
|
k = Complex128
|
||||||
case UntypedString:
|
case UntypedString:
|
||||||
k = String
|
k = String
|
||||||
default:
|
|
||||||
unreachable()
|
|
||||||
}
|
}
|
||||||
typ = Typ[k]
|
typ = Typ[k]
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (check *checker) assignOperand(z, x *operand) {
|
func (check *checker) assignOperand(z, x *operand) {
|
||||||
if t, ok := x.typ.(*tuple); ok {
|
if t, ok := x.typ.(*Result); ok {
|
||||||
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
|
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
|
||||||
check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.list), x)
|
check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -95,7 +95,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
typ = x.typ
|
typ = x.typ
|
||||||
if obj.Kind == ast.Var && isUntyped(typ) {
|
if obj.Kind == ast.Var && isUntyped(typ) {
|
||||||
typ = defaultType(typ)
|
if x.isNil() {
|
||||||
|
check.errorf(x.pos(), "use of untyped nil")
|
||||||
|
x.mode = invalid
|
||||||
|
} else {
|
||||||
|
typ = defaultType(typ)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obj.Type = typ
|
obj.Type = typ
|
||||||
@ -177,12 +182,12 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, ok := x.typ.(*tuple); ok && len(lhs) == len(t.list) {
|
if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) {
|
||||||
// function result
|
// function result
|
||||||
x.mode = value
|
x.mode = value
|
||||||
for i, typ := range t.list {
|
for i, obj := range t.Values {
|
||||||
x.expr = nil // TODO(gri) should do better here
|
x.expr = nil // TODO(gri) should do better here
|
||||||
x.typ = typ
|
x.typ = obj.Type.(Type)
|
||||||
check.assign1to1(lhs[i], nil, &x, decl, iota)
|
check.assign1to1(lhs[i], nil, &x, decl, iota)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -429,7 +434,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||||||
var x operand
|
var x operand
|
||||||
tag := s.Tag
|
tag := s.Tag
|
||||||
if tag == nil {
|
if tag == nil {
|
||||||
// create true tag value and position it at the opening { of the switch
|
// use fake true tag value and position it at the opening { of the switch
|
||||||
tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
|
tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
|
||||||
}
|
}
|
||||||
check.expr(&x, tag, nil, -1)
|
check.expr(&x, tag, nil, -1)
|
||||||
@ -451,15 +456,15 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||||||
}
|
}
|
||||||
// If we have a constant case value, it must appear only
|
// If we have a constant case value, it must appear only
|
||||||
// once in the switch statement. Determine if there is a
|
// once in the switch statement. Determine if there is a
|
||||||
// duplicate entry, but only report an error there are no
|
// duplicate entry, but only report an error if there are
|
||||||
// other errors.
|
// no other errors.
|
||||||
var dupl token.Pos
|
var dupl token.Pos
|
||||||
if y.mode == constant {
|
if y.mode == constant {
|
||||||
// TODO(gri) This code doesn't work correctly for
|
// TODO(gri) This code doesn't work correctly for
|
||||||
// large integer, floating point, or
|
// large integer, floating point, or
|
||||||
// complex values - the respective struct
|
// complex values - the respective struct
|
||||||
// comparison is shallow. Need to use a
|
// comparisons are shallow. Need to use a
|
||||||
// has function to index the seen map.
|
// hash function to index the map.
|
||||||
dupl = seen[y.val]
|
dupl = seen[y.val]
|
||||||
seen[y.val] = y.pos()
|
seen[y.val] = y.pos()
|
||||||
}
|
}
|
||||||
@ -475,7 +480,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||||||
}
|
}
|
||||||
check.comparison(&y, &x, token.EQL)
|
check.comparison(&y, &x, token.EQL)
|
||||||
if y.mode != invalid && dupl.IsValid() {
|
if y.mode != invalid && dupl.IsValid() {
|
||||||
check.errorf(y.pos(), "%s is duplicate case in switch\n\tprevious case at %s",
|
check.errorf(y.pos(), "%s is duplicate case (previous at %s)",
|
||||||
&y, check.fset.Position(dupl))
|
&y, check.fset.Position(dupl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
src/pkg/exp/types/testdata/decls1.src
vendored
5
src/pkg/exp/types/testdata/decls1.src
vendored
@ -46,7 +46,7 @@ var (
|
|||||||
s14 = i << j /* ERROR "must be unsigned" */
|
s14 = i << j /* ERROR "must be unsigned" */
|
||||||
s18 = math.Pi * 10.0
|
s18 = math.Pi * 10.0
|
||||||
s19 = s1 /* ERROR "cannot call" */ ()
|
s19 = s1 /* ERROR "cannot call" */ ()
|
||||||
s20 = f0 /* ERROR "used as single value" */ ()
|
s20 = f0 /* ERROR "no value" */ ()
|
||||||
s21 = f6(1, s1, i)
|
s21 = f6(1, s1, i)
|
||||||
s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
|
s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ var (
|
|||||||
t17 math /* ERROR "not a type" */ .Pi
|
t17 math /* ERROR "not a type" */ .Pi
|
||||||
t18 float64 = math.Pi * 10.0
|
t18 float64 = math.Pi * 10.0
|
||||||
t19 int = t1 /* ERROR "cannot call" */ ()
|
t19 int = t1 /* ERROR "cannot call" */ ()
|
||||||
t20 int = f0 /* ERROR "used as single value" */ ()
|
t20 int = f0 /* ERROR "no value" */ ()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Various more complex expressions
|
// Various more complex expressions
|
||||||
@ -94,6 +94,7 @@ var (
|
|||||||
v10 byte = 1024 /* ERROR "overflows" */
|
v10 byte = 1024 /* ERROR "overflows" */
|
||||||
v11 = xx/yy*yy - xx
|
v11 = xx/yy*yy - xx
|
||||||
v12 = true && false
|
v12 = true && false
|
||||||
|
v13 = nil /* ERROR "use of untyped nil" */
|
||||||
)
|
)
|
||||||
|
|
||||||
// Multiple assignment expressions
|
// Multiple assignment expressions
|
||||||
|
4
src/pkg/exp/types/testdata/expr0.src
vendored
4
src/pkg/exp/types/testdata/expr0.src
vendored
@ -63,6 +63,7 @@ var (
|
|||||||
u16 = &u0
|
u16 = &u0
|
||||||
u17 = *u16
|
u17 = *u16
|
||||||
u18 = <-u16 /* ERROR "cannot receive" */
|
u18 = <-u16 /* ERROR "cannot receive" */
|
||||||
|
u19 = ^uint(0)
|
||||||
|
|
||||||
// float64
|
// float64
|
||||||
f0 = float64(1)
|
f0 = float64(1)
|
||||||
@ -131,5 +132,4 @@ var (
|
|||||||
ch7 = <-ch
|
ch7 = <-ch
|
||||||
ch8 = <-rc
|
ch8 = <-rc
|
||||||
ch9 = <-sc /* ERROR "cannot receive" */
|
ch9 = <-sc /* ERROR "cannot receive" */
|
||||||
|
)
|
||||||
)
|
|
||||||
|
11
src/pkg/exp/types/testdata/expr2.src
vendored
11
src/pkg/exp/types/testdata/expr2.src
vendored
@ -6,6 +6,17 @@
|
|||||||
|
|
||||||
package expr2
|
package expr2
|
||||||
|
|
||||||
|
func _bool() {
|
||||||
|
const t = true == true
|
||||||
|
const f = true == false
|
||||||
|
_ = t /* ERROR "cannot compare" */ < f
|
||||||
|
_ = 0 /* ERROR "cannot convert" */ == t
|
||||||
|
var b bool
|
||||||
|
var x, y float32
|
||||||
|
b = x < y
|
||||||
|
_ = struct{b bool}{x < y}
|
||||||
|
}
|
||||||
|
|
||||||
// corner cases
|
// corner cases
|
||||||
var (
|
var (
|
||||||
v0 = nil /* ERROR "cannot compare" */ == nil
|
v0 = nil /* ERROR "cannot compare" */ == nil
|
||||||
|
61
src/pkg/exp/types/testdata/expr3.src
vendored
61
src/pkg/exp/types/testdata/expr3.src
vendored
@ -286,3 +286,64 @@ func type_asserts() {
|
|||||||
_ = t.(T2 /* ERROR "wrong type for method m" */ )
|
_ = t.(T2 /* ERROR "wrong type for method m" */ )
|
||||||
_ = t.(I2 /* ERROR "wrong type for method m" */ )
|
_ = t.(I2 /* ERROR "wrong type for method m" */ )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func f0() {}
|
||||||
|
func f1(x int) {}
|
||||||
|
func f2(u float32, s string) {}
|
||||||
|
func fs(s []byte) {}
|
||||||
|
func fv(x ...int) {}
|
||||||
|
func fi(x ... interface{}) {}
|
||||||
|
|
||||||
|
func g0() {}
|
||||||
|
func g1() int { return 0}
|
||||||
|
func g2() (u float32, s string) { return }
|
||||||
|
func gs() []byte { return nil }
|
||||||
|
|
||||||
|
func _calls() {
|
||||||
|
var x int
|
||||||
|
var y float32
|
||||||
|
var s []int
|
||||||
|
|
||||||
|
f0()
|
||||||
|
_ = f0 /* ERROR "used as value" */ ()
|
||||||
|
f0(g0 /* ERROR "too many arguments" */ )
|
||||||
|
|
||||||
|
f1(0)
|
||||||
|
f1(x)
|
||||||
|
f1(10.0)
|
||||||
|
f1 /* ERROR "too few arguments" */ ()
|
||||||
|
f1(x, y /* ERROR "too many arguments" */ )
|
||||||
|
f1(s /* ERROR "cannot assign" */ )
|
||||||
|
f1(x ... /* ERROR "cannot use ..." */ )
|
||||||
|
f1(g0 /* ERROR "used as value" */ ())
|
||||||
|
f1(g1())
|
||||||
|
// f1(g2()) // TODO(gri) missing position in error message
|
||||||
|
|
||||||
|
f2 /* ERROR "too few arguments" */ ()
|
||||||
|
f2 /* ERROR "too few arguments" */ (3.14)
|
||||||
|
f2(3.14, "foo")
|
||||||
|
f2(x /* ERROR "cannot assign" */ , "foo")
|
||||||
|
f2(g0 /* ERROR "used as value" */ ())
|
||||||
|
f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
|
||||||
|
f2(g2())
|
||||||
|
|
||||||
|
fs /* ERROR "too few arguments" */ ()
|
||||||
|
fs(g0 /* ERROR "used as value" */ ())
|
||||||
|
fs(g1 /* ERROR "cannot assign" */ ())
|
||||||
|
// fs(g2()) // TODO(gri) missing position in error message
|
||||||
|
fs(gs())
|
||||||
|
|
||||||
|
fv()
|
||||||
|
fv(1, 2.0, x)
|
||||||
|
fv(s /* ERROR "cannot assign" */ )
|
||||||
|
fv(s...)
|
||||||
|
fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
||||||
|
fv(gs /* ERROR "cannot assign" */ ())
|
||||||
|
fv(gs /* ERROR "cannot assign" */ ()...)
|
||||||
|
|
||||||
|
fi()
|
||||||
|
fi(1, 2.0, x, 3.14, "foo")
|
||||||
|
fi(g2())
|
||||||
|
fi(0, g2)
|
||||||
|
fi(0, g2 /* ERROR "2-valued expression" */ ())
|
||||||
|
}
|
@ -141,15 +141,15 @@ type Pointer struct {
|
|||||||
Base Type
|
Base Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// A tuple represents a multi-value function return.
|
// A Result represents a (multi-value) function call result.
|
||||||
// TODO(gri) use better name to avoid confusion (Go doesn't have tuples).
|
// TODO(gri) consider using an empty Result (Values == nil)
|
||||||
type tuple struct {
|
// as representation for the novalue operand mode.
|
||||||
|
type Result struct {
|
||||||
implementsType
|
implementsType
|
||||||
list []Type
|
Values ObjList // Signature.Results of the function called
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Signature represents a user-defined function type func(...) (...).
|
// A Signature represents a user-defined function type func(...) (...).
|
||||||
// TODO(gri) consider using "tuples" to represent parameters and results (see comment on tuples).
|
|
||||||
type Signature struct {
|
type Signature struct {
|
||||||
implementsType
|
implementsType
|
||||||
Recv *ast.Object // nil if not a method
|
Recv *ast.Object // nil if not a method
|
||||||
|
@ -155,7 +155,7 @@ var testExprs = []testEntry{
|
|||||||
dup("-f(10, 20)"),
|
dup("-f(10, 20)"),
|
||||||
dup("f(x + y, +3.1415)"),
|
dup("f(x + y, +3.1415)"),
|
||||||
{"func(a, b int) {}", "(func literal)"},
|
{"func(a, b int) {}", "(func literal)"},
|
||||||
{"func(a, b int) []int {}()[x]", "(func literal)()[x]"},
|
{"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
|
||||||
{"[]int{1, 2, 3}", "(composite literal)"},
|
{"[]int{1, 2, 3}", "(composite literal)"},
|
||||||
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
|
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
|
||||||
{"i.([]string)", "i.(...)"},
|
{"i.([]string)", "i.(...)"},
|
||||||
|
Loading…
Reference in New Issue
Block a user