mirror of
https://github.com/golang/go
synced 2024-11-19 00:34:40 -07:00
go.tools/go/types: record call-site specific types of built-in functions
Given a built-in call f(args), Info.Types now maps f to the call-site specific type of f (by looking at the argument types) if the built-in call is not producing a constant (at typecheck time) result. If the result is constant, the recorded type is invalid (a back-end won't need it). R=adonovan CC=golang-dev https://golang.org/cl/14598045
This commit is contained in:
parent
7b481db506
commit
0c45220917
@ -110,6 +110,11 @@ type Config struct {
|
|||||||
type Info struct {
|
type Info struct {
|
||||||
// Types maps expressions to their types. Identifiers on the
|
// Types maps expressions to their types. Identifiers on the
|
||||||
// lhs of declarations are collected in Objects, not Types.
|
// lhs of declarations are collected in Objects, not Types.
|
||||||
|
//
|
||||||
|
// For an expression denoting a predeclared built-in function
|
||||||
|
// the recorded signature is call-site specific. If the call
|
||||||
|
// result is not a constant, the recorded type is an argument-
|
||||||
|
// specific signature. Otherwise, the recorded type is invalid.
|
||||||
Types map[ast.Expr]Type
|
Types map[ast.Expr]Type
|
||||||
|
|
||||||
// Values maps constant expressions to their values.
|
// Values maps constant expressions to their values.
|
||||||
|
@ -87,6 +87,15 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isString(x.typ) {
|
if isString(x.typ) {
|
||||||
|
if check.Types != nil {
|
||||||
|
// TODO(gri, adonovan) change this to:
|
||||||
|
//
|
||||||
|
// sig := makeSig(S, S, NewSlice(Typ[Byte]))
|
||||||
|
// sig.isVariadic = true
|
||||||
|
//
|
||||||
|
// once ssa has been adjusted
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(S, S, x.typ))
|
||||||
|
}
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.typ = S
|
x.typ = S
|
||||||
break
|
break
|
||||||
@ -106,18 +115,23 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
}
|
}
|
||||||
arg(x, i)
|
arg(x, i)
|
||||||
}, nargs)
|
}, nargs)
|
||||||
|
// ok to continue even if check.arguments reported errors
|
||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.typ = S
|
x.typ = S
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
|
}
|
||||||
|
|
||||||
case _Cap, _Len:
|
case _Cap, _Len:
|
||||||
// cap(x)
|
// cap(x)
|
||||||
// len(x)
|
// len(x)
|
||||||
mode := invalid
|
mode := invalid
|
||||||
|
var typ Type
|
||||||
var val exact.Value
|
var val exact.Value
|
||||||
switch typ := implicitArrayDeref(x.typ.Underlying()).(type) {
|
switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
if isString(typ) && id == _Len {
|
if isString(t) && id == _Len {
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
mode = constant
|
mode = constant
|
||||||
val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
|
val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
|
||||||
@ -134,7 +148,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
// function calls; in this case s is not evaluated."
|
// function calls; in this case s is not evaluated."
|
||||||
if !check.containsCallsOrReceives(x.expr) {
|
if !check.containsCallsOrReceives(x.expr) {
|
||||||
mode = constant
|
mode = constant
|
||||||
val = exact.MakeInt64(typ.len)
|
val = exact.MakeInt64(t.len)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Slice, *Chan:
|
case *Slice, *Chan:
|
||||||
@ -150,9 +164,13 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
check.invalidArg(x.pos(), "%s for %s", x, bin.name)
|
check.invalidArg(x.pos(), "%s for %s", x, bin.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
x.mode = mode
|
x.mode = mode
|
||||||
x.typ = Typ[Int]
|
x.typ = Typ[Int]
|
||||||
x.val = val
|
x.val = val
|
||||||
|
if check.Types != nil && mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
|
||||||
|
}
|
||||||
|
|
||||||
case _Close:
|
case _Close:
|
||||||
// close(c)
|
// close(c)
|
||||||
@ -165,7 +183,11 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
|
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
x.mode = novalue
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, c))
|
||||||
|
}
|
||||||
|
|
||||||
case _Complex:
|
case _Complex:
|
||||||
// complex(x, y realT) complexT
|
// complex(x, y realT) complexT
|
||||||
@ -202,9 +224,9 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
x.mode = value
|
x.mode = value
|
||||||
}
|
}
|
||||||
|
|
||||||
realT := x.typ.Underlying().(*Basic)
|
realT := x.typ
|
||||||
complexT := Typ[Invalid]
|
complexT := Typ[Invalid]
|
||||||
switch realT.kind {
|
switch realT.Underlying().(*Basic).kind {
|
||||||
case Float32:
|
case Float32:
|
||||||
complexT = Typ[Complex64]
|
complexT = Typ[Complex64]
|
||||||
case Float64:
|
case Float64:
|
||||||
@ -223,7 +245,11 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
|
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
x.typ = complexT
|
x.typ = complexT
|
||||||
|
if check.Types != nil && x.mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT))
|
||||||
|
}
|
||||||
|
|
||||||
if x.mode != constant {
|
if x.mode != constant {
|
||||||
// The arguments have now their final types, which at run-
|
// The arguments have now their final types, which at run-
|
||||||
@ -270,6 +296,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
|
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.typ = Typ[Int]
|
x.typ = Typ[Int]
|
||||||
|
if check.Types != nil {
|
||||||
|
S := NewSlice(dst)
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, S, S))
|
||||||
|
}
|
||||||
|
|
||||||
case _Delete:
|
case _Delete:
|
||||||
// delete(m, k)
|
// delete(m, k)
|
||||||
@ -282,11 +312,16 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !x.isAssignableTo(check.conf, m.key) {
|
if !x.isAssignableTo(check.conf, m.key) {
|
||||||
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
x.mode = novalue
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
|
||||||
|
}
|
||||||
|
|
||||||
case _Imag, _Real:
|
case _Imag, _Real:
|
||||||
// imag(complexT) realT
|
// imag(complexT) realT
|
||||||
@ -304,7 +339,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
} else {
|
} else {
|
||||||
x.mode = value
|
x.mode = value
|
||||||
}
|
}
|
||||||
k := Invalid
|
var k BasicKind
|
||||||
switch x.typ.Underlying().(*Basic).kind {
|
switch x.typ.Underlying().(*Basic).kind {
|
||||||
case Complex64:
|
case Complex64:
|
||||||
k = Float32
|
k = Float32
|
||||||
@ -315,6 +350,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if check.Types != nil && x.mode != constant {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
|
||||||
|
}
|
||||||
x.typ = Typ[k]
|
x.typ = Typ[k]
|
||||||
|
|
||||||
case _Make:
|
case _Make:
|
||||||
@ -352,6 +391,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
}
|
}
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
x.typ = T
|
x.typ = T
|
||||||
|
if check.Types != nil {
|
||||||
|
params := [...]Type{T, Typ[Int], Typ[Int]}
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...))
|
||||||
|
}
|
||||||
|
|
||||||
case _New:
|
case _New:
|
||||||
// new(T)
|
// new(T)
|
||||||
@ -360,32 +403,64 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
if T == Typ[Invalid] {
|
if T == Typ[Invalid] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
x.typ = &Pointer{base: T}
|
x.typ = &Pointer{base: T}
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
|
||||||
|
}
|
||||||
|
|
||||||
case _Panic, _Print, _Println:
|
case _Panic:
|
||||||
// panic(x interface{})
|
// panic(x)
|
||||||
|
arg(x, 0)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(gri) arguments must be assignable to _
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
// TODO(gri) we need a global empty interface somewhere
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, new(Interface)))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Print, _Println:
|
||||||
// print(x, y, ...)
|
// print(x, y, ...)
|
||||||
// println(x, y, ...)
|
// println(x, y, ...)
|
||||||
for i := 1; i < nargs; i++ {
|
var params []Type
|
||||||
arg(x, i)
|
if nargs > 0 {
|
||||||
if x.mode == invalid {
|
params = make([]Type, nargs)
|
||||||
return
|
params[0] = x.typ // first argument already evaluated
|
||||||
|
for i := 1; i < nargs; i++ {
|
||||||
|
arg(x, i)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params[i] = x.typ
|
||||||
|
// TODO(gri) arguments must be assignable to _
|
||||||
}
|
}
|
||||||
// TODO(gri) arguments must be assignable to _
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x.mode = novalue
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
|
||||||
|
}
|
||||||
|
|
||||||
case _Recover:
|
case _Recover:
|
||||||
// recover() interface{}
|
// recover() interface{}
|
||||||
x.mode = value
|
x.mode = value
|
||||||
x.typ = new(Interface)
|
x.typ = new(Interface) // TODO(gri) we need a global empty interface somewhere
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
||||||
|
}
|
||||||
|
|
||||||
case _Alignof:
|
case _Alignof:
|
||||||
// unsafe.Alignof(x T) uintptr, where x must be a variable
|
// unsafe.Alignof(x T) uintptr
|
||||||
|
// TODO(gri) argument must be assignable to _
|
||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(check.conf.alignof(x.typ))
|
x.val = exact.MakeInt64(check.conf.alignof(x.typ))
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
case _Offsetof:
|
case _Offsetof:
|
||||||
// unsafe.Offsetof(x T) uintptr, where x must be a selector
|
// unsafe.Offsetof(x T) uintptr, where x must be a selector
|
||||||
@ -424,12 +499,15 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(offs)
|
x.val = exact.MakeInt64(offs)
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
case _Sizeof:
|
case _Sizeof:
|
||||||
// unsafe.Sizeof(x T) uintptr
|
// unsafe.Sizeof(x T) uintptr
|
||||||
|
// TODO(gri) argument must be assignable to _
|
||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
|
x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
case _Assert:
|
case _Assert:
|
||||||
// assert(pred) causes a typechecker error if pred is false.
|
// assert(pred) causes a typechecker error if pred is false.
|
||||||
@ -447,6 +525,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
check.errorf(call.Pos(), "%s failed", call)
|
check.errorf(call.Pos(), "%s failed", call)
|
||||||
// compile-time assertion failure - safe to continue
|
// compile-time assertion failure - safe to continue
|
||||||
}
|
}
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
case _Trace:
|
case _Trace:
|
||||||
// trace(x, y, z, ...) dumps the positions, expressions, and
|
// trace(x, y, z, ...) dumps the positions, expressions, and
|
||||||
@ -466,6 +545,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
check.dump("%s: %s", x1.pos(), x1)
|
check.dump("%s: %s", x1.pos(), x1)
|
||||||
x1 = &t // use incoming x only for first argument
|
x1 = &t // use incoming x only for first argument
|
||||||
}
|
}
|
||||||
|
// trace is only available in test mode - no need to record signature
|
||||||
|
|
||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
@ -475,15 +555,16 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// makeSig makes a signature for the given argument and result types.
|
// makeSig makes a signature for the given argument and result types.
|
||||||
// res may be nil.
|
// Default types are used for untyped arguments, and res may be nil.
|
||||||
func makeSig(res Type, args ...Type) *Signature {
|
func makeSig(res Type, args ...Type) *Signature {
|
||||||
list := make([]*Var, len(args))
|
list := make([]*Var, len(args))
|
||||||
for i, param := range args {
|
for i, param := range args {
|
||||||
list[i] = NewVar(token.NoPos, nil, "", param)
|
list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
|
||||||
}
|
}
|
||||||
params := NewTuple(list...)
|
params := NewTuple(list...)
|
||||||
var result *Tuple
|
var result *Tuple
|
||||||
if res != nil {
|
if res != nil {
|
||||||
|
assert(!isUntyped(res))
|
||||||
result = NewTuple(NewVar(token.NoPos, nil, "", res))
|
result = NewTuple(NewVar(token.NoPos, nil, "", res))
|
||||||
}
|
}
|
||||||
return &Signature{params: params, results: result}
|
return &Signature{params: params, results: result}
|
||||||
|
194
go/types/builtins_test.go
Normal file
194
go/types/builtins_test.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var builtinCalls = []struct {
|
||||||
|
id builtinId
|
||||||
|
src string
|
||||||
|
sig string
|
||||||
|
}{
|
||||||
|
{_Append, `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
|
||||||
|
{_Append, `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{_Append, `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{_Append, `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
|
||||||
|
{_Append, `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string) []byte`},
|
||||||
|
{_Append, `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, string) p.T`},
|
||||||
|
|
||||||
|
{_Cap, `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
|
||||||
|
{_Cap, `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
|
||||||
|
{_Cap, `var s []int64; _ = cap(s)`, `func([]int64) int`},
|
||||||
|
{_Cap, `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
|
||||||
|
|
||||||
|
{_Len, `_ = len("foo")`, `invalid type`}, // constant
|
||||||
|
{_Len, `var s string; _ = len(s)`, `func(string) int`},
|
||||||
|
{_Len, `var s [10]int; _ = len(s)`, `invalid type`}, // constant
|
||||||
|
{_Len, `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
|
||||||
|
{_Len, `var s []int64; _ = len(s)`, `func([]int64) int`},
|
||||||
|
{_Len, `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
|
||||||
|
{_Len, `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
|
||||||
|
|
||||||
|
{_Close, `var c chan int; close(c)`, `func(chan int)`},
|
||||||
|
{_Close, `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
|
||||||
|
|
||||||
|
{_Complex, `_ = complex(1, 0)`, `invalid type`}, // constant
|
||||||
|
{_Complex, `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
|
||||||
|
{_Complex, `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
|
||||||
|
{_Complex, `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
|
||||||
|
{_Complex, `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
|
||||||
|
|
||||||
|
{_Copy, `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
|
||||||
|
{_Copy, `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func([][]int, [][]int) int`},
|
||||||
|
|
||||||
|
{_Delete, `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
|
||||||
|
{_Delete, `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
|
||||||
|
|
||||||
|
{_Imag, `_ = imag(1i)`, `invalid type`}, // constant
|
||||||
|
{_Imag, `var c complex64; _ = imag(c)`, `func(complex64) float32`},
|
||||||
|
{_Imag, `var c complex128; _ = imag(c)`, `func(complex128) float64`},
|
||||||
|
{_Imag, `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
|
||||||
|
{_Imag, `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{_Real, `_ = real(1i)`, `invalid type`}, // constant
|
||||||
|
{_Real, `var c complex64; _ = real(c)`, `func(complex64) float32`},
|
||||||
|
{_Real, `var c complex128; _ = real(c)`, `func(complex128) float64`},
|
||||||
|
{_Real, `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
|
||||||
|
{_Real, `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{_Make, `_ = make([]int, 10)`, `func([]int, int) []int`},
|
||||||
|
{_Make, `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
|
||||||
|
|
||||||
|
{_New, `_ = new(int)`, `func(int) *int`},
|
||||||
|
{_New, `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||||
|
|
||||||
|
{_Panic, `panic(0)`, `func(interface{})`},
|
||||||
|
{_Panic, `panic("foo")`, `func(interface{})`},
|
||||||
|
|
||||||
|
{_Print, `print()`, `func()`},
|
||||||
|
{_Print, `print(0)`, `func(int)`},
|
||||||
|
{_Print, `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{_Println, `println()`, `func()`},
|
||||||
|
{_Println, `println(0)`, `func(int)`},
|
||||||
|
{_Println, `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{_Recover, `recover()`, `func() interface{}`},
|
||||||
|
{_Recover, `_ = recover()`, `func() interface{}`},
|
||||||
|
|
||||||
|
{_Alignof, `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
|
||||||
|
{_Alignof, `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{_Offsetof, `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
|
||||||
|
{_Offsetof, `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{_Sizeof, `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
|
||||||
|
{_Sizeof, `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{_Assert, `assert(true)`, `invalid type`}, // constant
|
||||||
|
{_Assert, `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
// no tests for trace since it produces output as a side-effect
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuiltinSignatures(t *testing.T) {
|
||||||
|
defPredeclaredTestFuncs()
|
||||||
|
|
||||||
|
seen := map[builtinId]bool{_Trace: true} // no test for _Trace; add it manually
|
||||||
|
for _, call := range builtinCalls {
|
||||||
|
testBuiltinSignature(t, call.id, call.src, call.sig)
|
||||||
|
seen[call.id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we didn't miss one
|
||||||
|
for i := range predeclaredFuncs {
|
||||||
|
if id := builtinId(i); !seen[id] {
|
||||||
|
t.Errorf("missing test for %s", predeclaredFuncs[id].name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) {
|
||||||
|
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
|
||||||
|
f, err := parser.ParseFile(fset, "", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf Config
|
||||||
|
objects := make(map[*ast.Ident]Object)
|
||||||
|
types := make(map[ast.Expr]Type)
|
||||||
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects, Types: types})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find called function
|
||||||
|
n := 0
|
||||||
|
var fun ast.Expr
|
||||||
|
for x, _ := range types {
|
||||||
|
if call, _ := x.(*ast.CallExpr); call != nil {
|
||||||
|
fun = call.Fun
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("%s: got %d CallExprs; want 1", src0, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check recorded types for fun and descendents (may be parenthesized)
|
||||||
|
for {
|
||||||
|
// the recorded type for the built-in must match the wanted signature
|
||||||
|
typ := types[fun]
|
||||||
|
if typ == nil {
|
||||||
|
t.Errorf("%s: no type recorded for %s", src0, exprString(fun))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got := typ.String(); got != want {
|
||||||
|
t.Errorf("%s: got type %s; want %s", src0, got, want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// called function must be a (possibly parenthesized, qualified)
|
||||||
|
// identifier denoting the expected built-in
|
||||||
|
switch p := fun.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
obj := objects[p]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: no object found for %s", src0, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bin, _ := obj.(*Builtin)
|
||||||
|
if bin == nil {
|
||||||
|
t.Errorf("%s: %s does not denote a built-in", src0, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bin.id != id {
|
||||||
|
t.Errorf("%s: got built-in %s; want %s", src0, bin.name, predeclaredFuncs[id].name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
fun = p.X // unpack
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
// built-in from package unsafe - ignore details
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: invalid function call", src0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -78,6 +78,24 @@ func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *checker) recordBuiltinType(f ast.Expr, sig *Signature) {
|
||||||
|
// f must be a (possibly parenthesized) identifier denoting a built-in
|
||||||
|
// (built-ins in package unsafe always produce a constant result and
|
||||||
|
// we don't record their signatures, so we don't see qualified idents
|
||||||
|
// here): record the signature for f and possible children.
|
||||||
|
for {
|
||||||
|
check.recordTypeAndValue(f, sig, nil)
|
||||||
|
switch p := f.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return // we're done
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
f = p.X
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) {
|
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) {
|
||||||
assert(x != nil && isTyped(t1) && isTyped(t2) && isBoolean(t2))
|
assert(x != nil && isTyped(t1) && isTyped(t2) && isBoolean(t2))
|
||||||
if m := check.Types; m != nil {
|
if m := check.Types; m != nil {
|
||||||
|
@ -247,16 +247,9 @@ func checkFiles(t *testing.T, testfiles []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var testBuiltinsDeclared = false
|
|
||||||
|
|
||||||
func TestCheck(t *testing.T) {
|
func TestCheck(t *testing.T) {
|
||||||
// Declare builtins for testing.
|
// Declare builtins for testing.
|
||||||
// Not done in an init func to avoid an init race with
|
defPredeclaredTestFuncs()
|
||||||
// the construction of the Universe var.
|
|
||||||
if !testBuiltinsDeclared {
|
|
||||||
testBuiltinsDeclared = true
|
|
||||||
defPredeclaredTestFuncs()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If explicit test files are specified, only check those.
|
// If explicit test files are specified, only check those.
|
||||||
if files := *testFiles; files != "" {
|
if files := *testFiles; files != "" {
|
||||||
|
@ -133,6 +133,6 @@ func f() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n != 2 {
|
if n != 2 {
|
||||||
t.Errorf("got %d call CallExprs; want 2", n)
|
t.Errorf("got %d CallExprs; want 2", n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
go/types/testdata/builtins.src
vendored
37
go/types/testdata/builtins.src
vendored
@ -139,6 +139,7 @@ func complex1() {
|
|||||||
var f32 float32
|
var f32 float32
|
||||||
var f64 float64
|
var f64 float64
|
||||||
var c64 complex64
|
var c64 complex64
|
||||||
|
var c128 complex128
|
||||||
_ = complex() // ERROR not enough arguments
|
_ = complex() // ERROR not enough arguments
|
||||||
_ = complex(1) // ERROR not enough arguments
|
_ = complex(1) // ERROR not enough arguments
|
||||||
_ = complex(true /* ERROR invalid argument */ , 0)
|
_ = complex(true /* ERROR invalid argument */ , 0)
|
||||||
@ -157,8 +158,8 @@ func complex1() {
|
|||||||
_ = complex(f64, 1)
|
_ = complex(f64, 1)
|
||||||
_ = complex(f64, 1.0)
|
_ = complex(f64, 1.0)
|
||||||
_ = complex(f64, 'a')
|
_ = complex(f64, 'a')
|
||||||
_ = complex(f32 /* ERROR mismatched types */, f64)
|
_ = complex(f32 /* ERROR mismatched types */ , f64)
|
||||||
_ = complex(f64 /* ERROR mismatched types */, f32)
|
_ = complex(f64 /* ERROR mismatched types */ , f32)
|
||||||
_ = complex(1, 1)
|
_ = complex(1, 1)
|
||||||
_ = complex(1, 1.1)
|
_ = complex(1, 1.1)
|
||||||
_ = complex(1, 'a')
|
_ = complex(1, 'a')
|
||||||
@ -185,6 +186,19 @@ func complex1() {
|
|||||||
const _ = complex /* ERROR not constant */ (1 /* ERROR integer */ <<s, 0)
|
const _ = complex /* ERROR not constant */ (1 /* ERROR integer */ <<s, 0)
|
||||||
var _ int = complex /* ERROR cannot initialize */ (1 /* ERROR integer */ <<s, 0)
|
var _ int = complex /* ERROR cannot initialize */ (1 /* ERROR integer */ <<s, 0)
|
||||||
|
|
||||||
|
// floating-point argument types must be identical
|
||||||
|
type F32 float32
|
||||||
|
type F64 float64
|
||||||
|
var x32 F32
|
||||||
|
var x64 F64
|
||||||
|
c64 = complex(x32, x32)
|
||||||
|
_ = complex(x32 /* ERROR mismatched types */ , f32)
|
||||||
|
_ = complex(f32 /* ERROR mismatched types */ , x32)
|
||||||
|
c128 = complex(x64, x64)
|
||||||
|
_ = c128
|
||||||
|
_ = complex(x64 /* ERROR mismatched types */ , f64)
|
||||||
|
_ = complex(f64 /* ERROR mismatched types */ , x64)
|
||||||
|
|
||||||
var t []float32
|
var t []float32
|
||||||
_ = complex(t... /* ERROR invalid use of \.\.\. */ )
|
_ = complex(t... /* ERROR invalid use of \.\.\. */ )
|
||||||
}
|
}
|
||||||
@ -217,6 +231,9 @@ func copy1() {
|
|||||||
|
|
||||||
var t [][]int
|
var t [][]int
|
||||||
copy(t, t)
|
copy(t, t)
|
||||||
|
copy(t /* ERROR copy expects slice arguments */ , nil)
|
||||||
|
copy(nil /* ERROR copy expects slice arguments */ , t)
|
||||||
|
copy(nil /* ERROR copy expects slice arguments */ , nil)
|
||||||
copy(t... /* ERROR invalid use of \.\.\. */ )
|
copy(t... /* ERROR invalid use of \.\.\. */ )
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,6 +294,14 @@ func imag1() {
|
|||||||
imag /* ERROR not used */ (c64)
|
imag /* ERROR not used */ (c64)
|
||||||
_, _ = f32, f64
|
_, _ = f32, f64
|
||||||
|
|
||||||
|
// complex type may not be predeclared
|
||||||
|
type C64 complex64
|
||||||
|
type C128 complex128
|
||||||
|
var x64 C64
|
||||||
|
var x128 C128
|
||||||
|
f32 = imag(x64)
|
||||||
|
f64 = imag(x128)
|
||||||
|
|
||||||
var s []complex64
|
var s []complex64
|
||||||
_ = imag(s... /* ERROR invalid use of \.\.\. */ )
|
_ = imag(s... /* ERROR invalid use of \.\.\. */ )
|
||||||
}
|
}
|
||||||
@ -504,6 +529,14 @@ func real1() {
|
|||||||
f64 = real /* ERROR cannot assign */ (c64)
|
f64 = real /* ERROR cannot assign */ (c64)
|
||||||
real /* ERROR not used */ (c64)
|
real /* ERROR not used */ (c64)
|
||||||
|
|
||||||
|
// complex type may not be predeclared
|
||||||
|
type C64 complex64
|
||||||
|
type C128 complex128
|
||||||
|
var x64 C64
|
||||||
|
var x128 C128
|
||||||
|
f32 = imag(x64)
|
||||||
|
f64 = imag(x128)
|
||||||
|
|
||||||
var s []complex64
|
var s []complex64
|
||||||
_ = real(s... /* ERROR invalid use of \.\.\. */ )
|
_ = real(s... /* ERROR invalid use of \.\.\. */ )
|
||||||
_, _ = f32, f64
|
_, _ = f32, f64
|
||||||
|
@ -164,6 +164,9 @@ func defPredeclaredFuncs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func defPredeclaredTestFuncs() {
|
func defPredeclaredTestFuncs() {
|
||||||
|
if Universe.Lookup("assert") != nil {
|
||||||
|
return // already defined
|
||||||
|
}
|
||||||
def(newBuiltin(_Assert))
|
def(newBuiltin(_Assert))
|
||||||
def(newBuiltin(_Trace))
|
def(newBuiltin(_Trace))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user