1234 lines
31 KiB
Go
1234 lines
31 KiB
Go
package interp
|
|
|
|
import (
|
|
"errors"
|
|
"go/constant"
|
|
"math"
|
|
"reflect"
|
|
)
|
|
|
|
type opPredicates map[action]func(reflect.Type) bool
|
|
|
|
// typecheck handles all type checking following "go/types" logic.
|
|
//
|
|
// Due to variant type systems (itype vs reflect.Type) a single
|
|
// type system should used, namely reflect.Type with exception
|
|
// of the untyped flag on itype.
|
|
type typecheck struct{}
|
|
|
|
// op type checks an expression against a set of expression predicates.
|
|
func (check typecheck) op(p opPredicates, a action, n, c *node, t reflect.Type) error {
|
|
if pred := p[a]; pred != nil {
|
|
if !pred(t) {
|
|
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, c.typ.id())
|
|
}
|
|
} else {
|
|
return n.cfgErrorf("invalid operation: unknown operator %v", n.action)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// assignment checks if n can be assigned to typ.
|
|
//
|
|
// Use typ == nil to indicate assignment to an untyped blank identifier.
|
|
func (check typecheck) assignment(n *node, typ *itype, context string) error {
|
|
if n.typ.untyped {
|
|
if typ == nil || isInterface(typ) {
|
|
if typ == nil && n.typ.cat == nilT {
|
|
return n.cfgErrorf("use of untyped nil in %s", context)
|
|
}
|
|
typ = n.typ.defaultType()
|
|
}
|
|
if err := check.convertUntyped(n, typ); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if typ == nil {
|
|
return nil
|
|
}
|
|
|
|
if !n.typ.assignableTo(typ) {
|
|
if context == "" {
|
|
return n.cfgErrorf("cannot use type %s as type %s", n.typ.id(), typ.id())
|
|
}
|
|
return n.cfgErrorf("cannot use type %s as type %s in %s", n.typ.id(), typ.id(), context)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// assignExpr type checks an assign expression.
|
|
//
|
|
// This is done per pair of assignments.
|
|
func (check typecheck) assignExpr(n, dest, src *node) error {
|
|
if n.action == aAssign {
|
|
isConst := n.anc.kind == constDecl
|
|
if !isConst {
|
|
// var operations must be typed
|
|
dest.typ = dest.typ.defaultType()
|
|
}
|
|
|
|
return check.assignment(src, dest.typ, "assignment")
|
|
}
|
|
|
|
// assignment operations.
|
|
if n.nleft > 1 || n.nright > 1 {
|
|
return n.cfgErrorf("assignment operation %s requires single-valued expressions", n.action)
|
|
}
|
|
|
|
return check.binaryExpr(n)
|
|
}
|
|
|
|
// addressExpr type checks a unary address expression.
|
|
func (check typecheck) addressExpr(n *node) error {
|
|
c0 := n.child[0]
|
|
found := false
|
|
for !found {
|
|
switch c0.kind {
|
|
case parenExpr:
|
|
c0 = c0.child[0]
|
|
continue
|
|
case selectorExpr:
|
|
c0 = c0.child[1]
|
|
continue
|
|
case starExpr:
|
|
c0 = c0.child[0]
|
|
continue
|
|
case indexExpr, sliceExpr:
|
|
c := c0.child[0]
|
|
if isArray(c.typ) || isMap(c.typ) {
|
|
c0 = c
|
|
continue
|
|
}
|
|
case compositeLitExpr, identExpr:
|
|
found = true
|
|
continue
|
|
}
|
|
return n.cfgErrorf("invalid operation: cannot take address of %s [kind: %s]", c0.typ.id(), kinds[c0.kind])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// starExpr type checks a star expression on a variable.
|
|
func (check typecheck) starExpr(n *node) error {
|
|
if n.typ.TypeOf().Kind() != reflect.Ptr {
|
|
return n.cfgErrorf("invalid operation: cannot indirect %q", n.name())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var unaryOpPredicates = opPredicates{
|
|
aPos: isNumber,
|
|
aNeg: isNumber,
|
|
aBitNot: isInt,
|
|
aNot: isBoolean,
|
|
}
|
|
|
|
// unaryExpr type checks a unary expression.
|
|
func (check typecheck) unaryExpr(n *node) error {
|
|
c0 := n.child[0]
|
|
t0 := c0.typ.TypeOf()
|
|
|
|
if n.action == aRecv {
|
|
if !isChan(c0.typ) {
|
|
return n.cfgErrorf("invalid operation: cannot receive from non-channel %s", c0.typ.id())
|
|
}
|
|
if isSendChan(c0.typ) {
|
|
return n.cfgErrorf("invalid operation: cannot receive from send-only channel %s", c0.typ.id())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := check.op(unaryOpPredicates, n.action, n, c0, t0); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// shift type checks a shift binary expression.
|
|
func (check typecheck) shift(n *node) error {
|
|
c0, c1 := n.child[0], n.child[1]
|
|
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
|
|
|
var v0 constant.Value
|
|
if c0.typ.untyped && c0.rval.IsValid() {
|
|
v0 = constant.ToInt(c0.rval.Interface().(constant.Value))
|
|
c0.rval = reflect.ValueOf(v0)
|
|
}
|
|
|
|
if !(c0.typ.untyped && v0 != nil && v0.Kind() == constant.Int || isInt(t0)) {
|
|
return n.cfgErrorf("invalid operation: shift of type %v", c0.typ.id())
|
|
}
|
|
|
|
switch {
|
|
case c1.typ.untyped:
|
|
if err := check.convertUntyped(c1, &itype{cat: uintT, name: "uint"}); err != nil {
|
|
return n.cfgErrorf("invalid operation: shift count type %v, must be integer", c1.typ.id())
|
|
}
|
|
case isInt(t1):
|
|
// nothing to do
|
|
default:
|
|
return n.cfgErrorf("invalid operation: shift count type %v, must be integer", c1.typ.id())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// comparison type checks a comparison binary expression.
|
|
func (check typecheck) comparison(n *node) error {
|
|
c0, c1 := n.child[0], n.child[1]
|
|
|
|
if !c0.typ.assignableTo(c1.typ) && !c1.typ.assignableTo(c0.typ) {
|
|
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
|
}
|
|
|
|
ok := false
|
|
switch n.action {
|
|
case aEqual, aNotEqual:
|
|
ok = c0.typ.comparable() && c1.typ.comparable() || c0.typ.isNil() && c1.typ.hasNil() || c1.typ.isNil() && c0.typ.hasNil()
|
|
case aLower, aLowerEqual, aGreater, aGreaterEqual:
|
|
ok = c0.typ.ordered() && c1.typ.ordered()
|
|
}
|
|
if !ok {
|
|
typ := c0.typ
|
|
if typ.isNil() {
|
|
typ = c1.typ
|
|
}
|
|
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id(), ".")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var binaryOpPredicates = opPredicates{
|
|
aAdd: func(typ reflect.Type) bool { return isNumber(typ) || isString(typ) },
|
|
aSub: isNumber,
|
|
aMul: isNumber,
|
|
aQuo: isNumber,
|
|
aRem: isInt,
|
|
|
|
aAnd: isInt,
|
|
aOr: isInt,
|
|
aXor: isInt,
|
|
aAndNot: isInt,
|
|
|
|
aLand: isBoolean,
|
|
aLor: isBoolean,
|
|
}
|
|
|
|
// binaryExpr type checks a binary expression.
|
|
func (check typecheck) binaryExpr(n *node) error {
|
|
c0, c1 := n.child[0], n.child[1]
|
|
|
|
a := n.action
|
|
if isAssignAction(a) {
|
|
a--
|
|
}
|
|
|
|
if isShiftAction(a) {
|
|
return check.shift(n)
|
|
}
|
|
|
|
switch n.action {
|
|
case aRem:
|
|
if zeroConst(c1) {
|
|
return n.cfgErrorf("invalid operation: division by zero")
|
|
}
|
|
case aQuo:
|
|
if zeroConst(c1) {
|
|
return n.cfgErrorf("invalid operation: division by zero")
|
|
}
|
|
if c0.rval.IsValid() && c1.rval.IsValid() {
|
|
// Avoid constant conversions below to ensure correct constant integer quotient.
|
|
return nil
|
|
}
|
|
}
|
|
|
|
_ = check.convertUntyped(c0, c1.typ)
|
|
_ = check.convertUntyped(c1, c0.typ)
|
|
|
|
if isComparisonAction(a) {
|
|
return check.comparison(n)
|
|
}
|
|
|
|
if !c0.typ.equals(c1.typ) {
|
|
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
|
}
|
|
|
|
t0 := c0.typ.TypeOf()
|
|
if err := check.op(binaryOpPredicates, a, n, c0, t0); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func zeroConst(n *node) bool {
|
|
return n.typ.untyped && constant.Sign(n.rval.Interface().(constant.Value)) == 0
|
|
}
|
|
|
|
func (check typecheck) index(n *node, max int) error {
|
|
if err := check.convertUntyped(n, &itype{cat: intT, name: "int"}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !isInt(n.typ.TypeOf()) {
|
|
return n.cfgErrorf("index %s must be integer", n.typ.id())
|
|
}
|
|
|
|
if !n.rval.IsValid() || max < 1 {
|
|
return nil
|
|
}
|
|
|
|
if int(vInt(n.rval)) >= max {
|
|
return n.cfgErrorf("index %s is out of bounds", n.typ.id())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// arrayLitExpr type checks an array composite literal expression.
|
|
func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error {
|
|
visited := make(map[int]bool, len(child))
|
|
index := 0
|
|
for _, c := range child {
|
|
n := c
|
|
switch {
|
|
case c.kind == keyValueExpr:
|
|
if err := check.index(c.child[0], length); err != nil {
|
|
return c.cfgErrorf("index %s must be integer constant", c.child[0].typ.id())
|
|
}
|
|
n = c.child[1]
|
|
index = int(vInt(c.child[0].rval))
|
|
case length > 0 && index >= length:
|
|
return c.cfgErrorf("index %d is out of bounds (>= %d)", index, length)
|
|
}
|
|
|
|
if visited[index] {
|
|
return n.cfgErrorf("duplicate index %d in array or slice literal", index)
|
|
}
|
|
visited[index] = true
|
|
index++
|
|
|
|
if err := check.assignment(n, typ, "array or slice literal"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// mapLitExpr type checks an map composite literal expression.
|
|
func (check typecheck) mapLitExpr(child []*node, ktyp, vtyp *itype) error {
|
|
visited := make(map[interface{}]bool, len(child))
|
|
for _, c := range child {
|
|
if c.kind != keyValueExpr {
|
|
return c.cfgErrorf("missing key in map literal")
|
|
}
|
|
|
|
key, val := c.child[0], c.child[1]
|
|
if err := check.assignment(key, ktyp, "map literal"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if key.rval.IsValid() {
|
|
kval := key.rval.Interface()
|
|
if visited[kval] {
|
|
return c.cfgErrorf("duplicate key %s in map literal", kval)
|
|
}
|
|
visited[kval] = true
|
|
}
|
|
|
|
if err := check.assignment(val, vtyp, "map literal"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// structLitExpr type checks a struct composite literal expression.
|
|
func (check typecheck) structLitExpr(child []*node, typ *itype) error {
|
|
if len(child) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if child[0].kind == keyValueExpr {
|
|
// All children must be keyValueExpr
|
|
visited := make([]bool, len(typ.field))
|
|
for _, c := range child {
|
|
if c.kind != keyValueExpr {
|
|
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
|
}
|
|
|
|
key, val := c.child[0], c.child[1]
|
|
name := key.ident
|
|
if name == "" {
|
|
return c.cfgErrorf("invalid field name %s in struct literal", key.typ.id())
|
|
}
|
|
i := typ.fieldIndex(name)
|
|
if i < 0 {
|
|
return c.cfgErrorf("unknown field %s in struct literal", name)
|
|
}
|
|
field := typ.field[i]
|
|
|
|
if err := check.assignment(val, field.typ, "struct literal"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if visited[i] {
|
|
return c.cfgErrorf("duplicate field name %s in struct literal", name)
|
|
}
|
|
visited[i] = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// No children can be keyValueExpr
|
|
for i, c := range child {
|
|
if c.kind == keyValueExpr {
|
|
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
|
}
|
|
|
|
if i >= len(typ.field) {
|
|
return c.cfgErrorf("too many values in struct literal")
|
|
}
|
|
field := typ.field[i]
|
|
// TODO(nick): check if this field is not exported and in a different package.
|
|
|
|
if err := check.assignment(c, field.typ, "struct literal"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(child) < len(typ.field) {
|
|
return child[len(child)-1].cfgErrorf("too few values in struct literal")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// structBinLitExpr type checks a struct composite literal expression on a binary type.
|
|
func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
|
if len(child) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if child[0].kind == keyValueExpr {
|
|
// All children must be keyValueExpr
|
|
visited := make(map[string]bool, typ.NumField())
|
|
for _, c := range child {
|
|
if c.kind != keyValueExpr {
|
|
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
|
}
|
|
|
|
key, val := c.child[0], c.child[1]
|
|
name := key.ident
|
|
if name == "" {
|
|
return c.cfgErrorf("invalid field name %s in struct literal", key.typ.id())
|
|
}
|
|
field, ok := typ.FieldByName(name)
|
|
if !ok {
|
|
return c.cfgErrorf("unknown field %s in struct literal", name)
|
|
}
|
|
|
|
if err := check.assignment(val, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if visited[field.Name] {
|
|
return c.cfgErrorf("duplicate field name %s in struct literal", name)
|
|
}
|
|
visited[field.Name] = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// No children can be keyValueExpr
|
|
for i, c := range child {
|
|
if c.kind == keyValueExpr {
|
|
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
|
}
|
|
|
|
if i >= typ.NumField() {
|
|
return c.cfgErrorf("too many values in struct literal")
|
|
}
|
|
field := typ.Field(i)
|
|
if !canExport(field.Name) {
|
|
return c.cfgErrorf("implicit assignment to unexported field %s in %s literal", field.Name, typ)
|
|
}
|
|
|
|
if err := check.assignment(c, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(child) < typ.NumField() {
|
|
return child[len(child)-1].cfgErrorf("too few values in struct literal")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// sliceExpr type checks a slice expression.
|
|
func (check typecheck) sliceExpr(n *node) error {
|
|
c, child := n.child[0], n.child[1:]
|
|
|
|
t := c.typ.TypeOf()
|
|
var low, high, max *node
|
|
if len(child) >= 1 {
|
|
if n.action == aSlice {
|
|
low = child[0]
|
|
} else {
|
|
high = child[0]
|
|
}
|
|
}
|
|
if len(child) >= 2 {
|
|
if n.action == aSlice {
|
|
high = child[1]
|
|
} else {
|
|
max = child[1]
|
|
}
|
|
}
|
|
if len(child) == 3 && n.action == aSlice {
|
|
max = child[2]
|
|
}
|
|
|
|
l := -1
|
|
valid := false
|
|
switch t.Kind() {
|
|
case reflect.String:
|
|
valid = true
|
|
if c.rval.IsValid() {
|
|
l = len(vString(c.rval))
|
|
}
|
|
if max != nil {
|
|
return max.cfgErrorf("invalid operation: 3-index slice of string")
|
|
}
|
|
case reflect.Array:
|
|
valid = true
|
|
l = t.Len()
|
|
if c.kind != indexExpr && c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
|
|
return c.cfgErrorf("cannot slice type %s", c.typ.id())
|
|
}
|
|
case reflect.Slice:
|
|
valid = true
|
|
case reflect.Ptr:
|
|
if t.Elem().Kind() == reflect.Array {
|
|
valid = true
|
|
l = t.Elem().Len()
|
|
}
|
|
}
|
|
if !valid {
|
|
return c.cfgErrorf("cannot slice type %s", c.typ.id())
|
|
}
|
|
|
|
var ind [3]int64
|
|
for i, nod := range []*node{low, high, max} {
|
|
x := int64(-1)
|
|
switch {
|
|
case nod != nil:
|
|
max := -1
|
|
if l >= 0 {
|
|
max = l + 1
|
|
}
|
|
if err := check.index(nod, max); err != nil {
|
|
return err
|
|
}
|
|
if nod.rval.IsValid() {
|
|
x = vInt(nod.rval)
|
|
}
|
|
case i == 0:
|
|
x = 0
|
|
case l >= 0:
|
|
x = int64(l)
|
|
}
|
|
ind[i] = x
|
|
}
|
|
|
|
for i, x := range ind[:len(ind)-1] {
|
|
if x <= 0 {
|
|
continue
|
|
}
|
|
for _, y := range ind[i+1:] {
|
|
if y < 0 || x <= y {
|
|
continue
|
|
}
|
|
return n.cfgErrorf("invalid index values, must be low <= high <= max")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// typeAssertionExpr type checks a type assert expression.
|
|
func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
|
// TODO(nick): This type check is not complete and should be revisited once
|
|
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
|
// type check Named types as they cannot be asserted.
|
|
|
|
if n.typ.TypeOf().Kind() != reflect.Interface {
|
|
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
|
}
|
|
ims := n.typ.methods()
|
|
if len(ims) == 0 {
|
|
// Empty interface must be a dynamic check.
|
|
return nil
|
|
}
|
|
|
|
if isInterface(typ) {
|
|
// Asserting to an interface is a dynamic check as we must look to the
|
|
// underlying struct.
|
|
return nil
|
|
}
|
|
|
|
for name := range ims {
|
|
im := lookupFieldOrMethod(n.typ, name)
|
|
tm := lookupFieldOrMethod(typ, name)
|
|
if im == nil {
|
|
// This should not be possible.
|
|
continue
|
|
}
|
|
if tm == nil {
|
|
return n.cfgErrorf("impossible type assertion: %s does not implement %s (missing %v method)", typ.id(), n.typ.id(), name)
|
|
}
|
|
if tm.recv != nil && tm.recv.TypeOf().Kind() == reflect.Ptr && typ.TypeOf().Kind() != reflect.Ptr {
|
|
return n.cfgErrorf("impossible type assertion: %s does not implement %s as %q method has a pointer receiver", typ.id(), n.typ.id(), name)
|
|
}
|
|
|
|
err := n.cfgErrorf("impossible type assertion: %s does not implement %s", typ.id(), n.typ.id())
|
|
if im.numIn() != tm.numIn() || im.numOut() != tm.numOut() {
|
|
return err
|
|
}
|
|
for i := 0; i < im.numIn(); i++ {
|
|
if !im.in(i).equals(tm.in(i)) {
|
|
return err
|
|
}
|
|
}
|
|
for i := 0; i < im.numOut(); i++ {
|
|
if !im.out(i).equals(tm.out(i)) {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// conversion type checks the conversion of n to typ.
|
|
func (check typecheck) conversion(n *node, typ *itype) error {
|
|
var c constant.Value
|
|
if n.rval.IsValid() {
|
|
if con, ok := n.rval.Interface().(constant.Value); ok {
|
|
c = con
|
|
}
|
|
}
|
|
|
|
var ok bool
|
|
switch {
|
|
case c != nil && isConstType(typ):
|
|
switch t := typ.TypeOf(); {
|
|
case representableConst(c, t):
|
|
ok = true
|
|
case isInt(n.typ.TypeOf()) && isString(t):
|
|
codepoint := int64(-1)
|
|
if i, ok := constant.Int64Val(c); ok {
|
|
codepoint = i
|
|
}
|
|
n.rval = reflect.ValueOf(constant.MakeString(string(rune(codepoint))))
|
|
ok = true
|
|
}
|
|
|
|
case n.typ.convertibleTo(typ):
|
|
ok = true
|
|
}
|
|
if !ok {
|
|
return n.cfgErrorf("cannot convert expression of type %s to type %s", n.typ.id(), typ.id())
|
|
}
|
|
if !n.typ.untyped || c == nil {
|
|
return nil
|
|
}
|
|
if isInterface(typ) || !isConstType(typ) {
|
|
typ = n.typ.defaultType()
|
|
}
|
|
return check.convertUntyped(n, typ)
|
|
}
|
|
|
|
type param struct {
|
|
nod *node
|
|
typ *itype
|
|
}
|
|
|
|
func (p param) Type() *itype {
|
|
if p.typ != nil {
|
|
return p.typ
|
|
}
|
|
return p.nod.typ
|
|
}
|
|
|
|
// unpackParams unpacks child parameters into a slice of param.
|
|
// If there is only 1 child and it is a callExpr with an n-value return,
|
|
// the return types are returned, otherwise the original child nodes are
|
|
// returned with nil typ.
|
|
func (check typecheck) unpackParams(child []*node) (params []param) {
|
|
if len(child) == 1 && isCall(child[0]) && child[0].child[0].typ.numOut() > 1 {
|
|
c0 := child[0]
|
|
ftyp := child[0].child[0].typ
|
|
for i := 0; i < ftyp.numOut(); i++ {
|
|
params = append(params, param{nod: c0, typ: ftyp.out(i)})
|
|
}
|
|
return params
|
|
}
|
|
|
|
for _, c := range child {
|
|
params = append(params, param{nod: c})
|
|
}
|
|
return params
|
|
}
|
|
|
|
var builtinFuncs = map[string]struct {
|
|
args int
|
|
variadic bool
|
|
}{
|
|
bltnAppend: {args: 1, variadic: true},
|
|
bltnCap: {args: 1, variadic: false},
|
|
bltnClose: {args: 1, variadic: false},
|
|
bltnComplex: {args: 2, variadic: false},
|
|
bltnImag: {args: 1, variadic: false},
|
|
bltnCopy: {args: 2, variadic: false},
|
|
bltnDelete: {args: 2, variadic: false},
|
|
bltnLen: {args: 1, variadic: false},
|
|
bltnMake: {args: 1, variadic: true},
|
|
bltnNew: {args: 1, variadic: false},
|
|
bltnPanic: {args: 1, variadic: false},
|
|
bltnPrint: {args: 0, variadic: true},
|
|
bltnPrintln: {args: 0, variadic: true},
|
|
bltnReal: {args: 1, variadic: false},
|
|
bltnRecover: {args: 0, variadic: false},
|
|
}
|
|
|
|
func (check typecheck) builtin(name string, n *node, child []*node, ellipsis bool) error {
|
|
fun := builtinFuncs[name]
|
|
if ellipsis && name != bltnAppend {
|
|
return n.cfgErrorf("invalid use of ... with builtin %s", name)
|
|
}
|
|
|
|
var params []param
|
|
nparams := len(child)
|
|
switch name {
|
|
case bltnMake, bltnNew:
|
|
// Special param handling
|
|
default:
|
|
params = check.unpackParams(child)
|
|
nparams = len(params)
|
|
}
|
|
|
|
if nparams < fun.args {
|
|
return n.cfgErrorf("not enough arguments in call to %s", name)
|
|
} else if !fun.variadic && nparams > fun.args {
|
|
return n.cfgErrorf("too many arguments for %s", name)
|
|
}
|
|
|
|
switch name {
|
|
case bltnAppend:
|
|
typ := params[0].Type()
|
|
t := typ.TypeOf()
|
|
if t.Kind() != reflect.Slice {
|
|
return params[0].nod.cfgErrorf("first argument to append must be slice; have %s", typ.id())
|
|
}
|
|
|
|
// Special case append([]byte, "test"...) is allowed.
|
|
t1 := params[1].Type()
|
|
if nparams == 2 && ellipsis && t.Elem().Kind() == reflect.Uint8 && t1.TypeOf().Kind() == reflect.String {
|
|
if t1.untyped {
|
|
return check.convertUntyped(params[1].nod, &itype{cat: stringT, name: "string"})
|
|
}
|
|
return nil
|
|
}
|
|
// We cannot check a recursive type.
|
|
if isRecursiveType(typ, typ.TypeOf()) {
|
|
return nil
|
|
}
|
|
|
|
fun := &node{
|
|
typ: &itype{
|
|
cat: funcT,
|
|
arg: []*itype{
|
|
typ,
|
|
{cat: variadicT, val: &itype{cat: valueT, rtype: t.Elem()}},
|
|
},
|
|
ret: []*itype{typ},
|
|
},
|
|
ident: "append",
|
|
}
|
|
return check.arguments(n, child, fun, ellipsis)
|
|
case bltnCap, bltnLen:
|
|
typ := arrayDeref(params[0].Type())
|
|
ok := false
|
|
switch typ.TypeOf().Kind() {
|
|
case reflect.Array, reflect.Slice, reflect.Chan:
|
|
ok = true
|
|
case reflect.String, reflect.Map:
|
|
ok = name == bltnLen
|
|
}
|
|
if !ok {
|
|
return params[0].nod.cfgErrorf("invalid argument for %s", name)
|
|
}
|
|
case bltnClose:
|
|
p := params[0]
|
|
typ := p.Type()
|
|
t := typ.TypeOf()
|
|
if t.Kind() != reflect.Chan {
|
|
return p.nod.cfgErrorf("invalid operation: non-chan type %s", p.nod.typ.id())
|
|
}
|
|
if t.ChanDir() == reflect.RecvDir {
|
|
return p.nod.cfgErrorf("invalid operation: cannot close receive-only channel")
|
|
}
|
|
case bltnComplex:
|
|
var err error
|
|
p0, p1 := params[0], params[1]
|
|
typ0, typ1 := p0.Type(), p1.Type()
|
|
switch {
|
|
case typ0.untyped && !typ1.untyped:
|
|
err = check.convertUntyped(p0.nod, typ1)
|
|
case !typ0.untyped && typ1.untyped:
|
|
err = check.convertUntyped(p1.nod, typ0)
|
|
case typ0.untyped && typ1.untyped:
|
|
fltType := &itype{cat: float64T, name: "float64"}
|
|
err = check.convertUntyped(p0.nod, fltType)
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = check.convertUntyped(p1.nod, fltType)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check we have the correct types after conversion.
|
|
typ0, typ1 = p0.Type(), p1.Type()
|
|
if !typ0.equals(typ1) {
|
|
return n.cfgErrorf("invalid operation: mismatched types %s and %s", typ0.id(), typ1.id())
|
|
}
|
|
if !isFloat(typ0.TypeOf()) {
|
|
return n.cfgErrorf("invalid operation: arguments have type %s, expected floating-point", typ0.id())
|
|
}
|
|
case bltnImag, bltnReal:
|
|
p := params[0]
|
|
typ := p.Type()
|
|
if typ.untyped {
|
|
if err := check.convertUntyped(p.nod, &itype{cat: complex128T, name: "complex128"}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
typ = p.Type()
|
|
if !isComplex(typ.TypeOf()) {
|
|
return p.nod.cfgErrorf("invalid argument type %s for %s", typ.id(), name)
|
|
}
|
|
case bltnCopy:
|
|
typ0, typ1 := params[0].Type(), params[1].Type()
|
|
var t0, t1 reflect.Type
|
|
if t := typ0.TypeOf(); t.Kind() == reflect.Slice {
|
|
t0 = t.Elem()
|
|
}
|
|
|
|
switch t := typ1.TypeOf(); t.Kind() {
|
|
case reflect.String:
|
|
t1 = reflect.TypeOf(byte(1))
|
|
case reflect.Slice:
|
|
t1 = t.Elem()
|
|
}
|
|
|
|
if t0 == nil || t1 == nil {
|
|
return n.cfgErrorf("copy expects slice arguments")
|
|
}
|
|
if !reflect.DeepEqual(t0, t1) {
|
|
return n.cfgErrorf("arguments to copy have different element types %s and %s", typ0.id(), typ1.id())
|
|
}
|
|
case bltnDelete:
|
|
typ := params[0].Type()
|
|
if typ.TypeOf().Kind() != reflect.Map {
|
|
return params[0].nod.cfgErrorf("first argument to delete must be map; have %s", typ.id())
|
|
}
|
|
ktyp := params[1].Type()
|
|
if !ktyp.assignableTo(typ.key) {
|
|
return params[1].nod.cfgErrorf("cannot use %s as type %s in delete", ktyp.id(), typ.key.id())
|
|
}
|
|
case bltnMake:
|
|
var min int
|
|
switch child[0].typ.TypeOf().Kind() {
|
|
case reflect.Slice:
|
|
min = 2
|
|
case reflect.Map, reflect.Chan:
|
|
min = 1
|
|
default:
|
|
return child[0].cfgErrorf("cannot make %s; type must be slice, map, or channel", child[0].typ.id())
|
|
}
|
|
if nparams < min {
|
|
return n.cfgErrorf("not enough arguments in call to make")
|
|
} else if nparams > min+1 {
|
|
return n.cfgErrorf("too many arguments for make")
|
|
}
|
|
|
|
var sizes []int
|
|
for _, c := range child[1:] {
|
|
if err := check.index(c, -1); err != nil {
|
|
return err
|
|
}
|
|
if c.rval.IsValid() {
|
|
sizes = append(sizes, int(vInt(c.rval)))
|
|
}
|
|
}
|
|
for len(sizes) == 2 && sizes[0] > sizes[1] {
|
|
return n.cfgErrorf("len larger than cap in make")
|
|
}
|
|
|
|
case bltnPanic:
|
|
return check.assignment(params[0].nod, &itype{cat: interfaceT}, "argument to panic")
|
|
case bltnPrint, bltnPrintln:
|
|
for _, param := range params {
|
|
if param.typ != nil {
|
|
continue
|
|
}
|
|
|
|
if err := check.assignment(param.nod, nil, "argument to "+name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case bltnRecover, bltnNew:
|
|
// Nothing to do.
|
|
default:
|
|
return n.cfgErrorf("unsupported builtin %s", name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// arrayDeref returns A if typ is *A, otherwise typ.
|
|
func arrayDeref(typ *itype) *itype {
|
|
if typ.cat == valueT && typ.TypeOf().Kind() == reflect.Ptr {
|
|
t := typ.TypeOf()
|
|
if t.Elem().Kind() == reflect.Array {
|
|
return &itype{cat: valueT, rtype: t.Elem()}
|
|
}
|
|
return typ
|
|
}
|
|
|
|
if typ.cat == ptrT && typ.val.cat == arrayT && typ.val.sizedef {
|
|
return typ.val
|
|
}
|
|
return typ
|
|
}
|
|
|
|
// arguments type checks the call expression arguments.
|
|
func (check typecheck) arguments(n *node, child []*node, fun *node, ellipsis bool) error {
|
|
params := check.unpackParams(child)
|
|
l := len(child)
|
|
if ellipsis {
|
|
if !fun.typ.isVariadic() {
|
|
return n.cfgErrorf("invalid use of ..., corresponding parameter is non-variadic")
|
|
}
|
|
if len(params) > l {
|
|
return child[0].cfgErrorf("cannot use ... with %d-valued %s", child[0].child[0].typ.numOut(), child[0].child[0].typ.id())
|
|
}
|
|
}
|
|
|
|
var cnt int
|
|
for i, param := range params {
|
|
ellip := i == l-1 && ellipsis
|
|
if err := check.argument(param, fun.typ, cnt, l, ellip); err != nil {
|
|
return err
|
|
}
|
|
cnt++
|
|
}
|
|
|
|
if fun.typ.isVariadic() {
|
|
cnt++
|
|
}
|
|
if cnt < fun.typ.numIn() {
|
|
return n.cfgErrorf("not enough arguments in call to %s", fun.name())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) error {
|
|
atyp := getArg(ftyp, i)
|
|
if atyp == nil {
|
|
return p.nod.cfgErrorf("too many arguments")
|
|
}
|
|
|
|
if p.typ == nil && isCall(p.nod) && p.nod.child[0].typ.numOut() != 1 {
|
|
if l == 1 {
|
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), getArgsID(ftyp))
|
|
}
|
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), atyp.id())
|
|
}
|
|
|
|
if ellipsis {
|
|
if i != ftyp.numIn()-1 {
|
|
return p.nod.cfgErrorf("can only use ... with matching parameter")
|
|
}
|
|
t := p.Type().TypeOf()
|
|
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) {
|
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: arrayT, val: atyp}).id())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if p.typ != nil {
|
|
if !p.typ.assignableTo(atyp) {
|
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), getArgsID(ftyp))
|
|
}
|
|
return nil
|
|
}
|
|
return check.assignment(p.nod, atyp, "")
|
|
}
|
|
|
|
func getArg(ftyp *itype, i int) *itype {
|
|
l := ftyp.numIn()
|
|
switch {
|
|
case ftyp.isVariadic() && i >= l-1:
|
|
arg := ftyp.in(l - 1).val
|
|
return arg
|
|
case i < l:
|
|
return ftyp.in(i)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getArgsID(ftyp *itype) string {
|
|
res := "("
|
|
for i, arg := range ftyp.arg {
|
|
if i > 0 {
|
|
res += ","
|
|
}
|
|
res += arg.id()
|
|
}
|
|
res += ")"
|
|
return res
|
|
}
|
|
|
|
var errCantConvert = errors.New("cannot convert")
|
|
|
|
func (check typecheck) convertUntyped(n *node, typ *itype) error {
|
|
if n.typ == nil || !n.typ.untyped || typ == nil {
|
|
return nil
|
|
}
|
|
|
|
convErr := n.cfgErrorf("cannot convert %s to %s", n.typ.id(), typ.id())
|
|
|
|
ntyp, ttyp := n.typ.TypeOf(), typ.TypeOf()
|
|
if typ.untyped {
|
|
// Both n and target are untyped.
|
|
nkind, tkind := ntyp.Kind(), ttyp.Kind()
|
|
if isNumber(ntyp) && isNumber(ttyp) {
|
|
if nkind < tkind {
|
|
n.typ = typ
|
|
}
|
|
} else if nkind != tkind {
|
|
return convErr
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
ityp *itype
|
|
rtyp reflect.Type
|
|
err error
|
|
)
|
|
switch {
|
|
case typ.isNil() && n.typ.isNil():
|
|
n.typ = typ
|
|
return nil
|
|
case isNumber(ttyp) || isString(ttyp) || isBoolean(ttyp):
|
|
ityp = typ
|
|
rtyp = ttyp
|
|
case isInterface(typ):
|
|
if n.typ.isNil() {
|
|
return nil
|
|
}
|
|
if len(n.typ.methods()) > 0 { // untyped cannot be set to iface
|
|
return convErr
|
|
}
|
|
ityp = n.typ.defaultType()
|
|
rtyp = ntyp
|
|
|
|
case isArray(typ) || isMap(typ) || isChan(typ) || isFunc(typ) || isPtr(typ):
|
|
// TODO(nick): above we are acting on itype, but really it is an rtype check. This is not clear which type
|
|
// plain we are in. Fix this later.
|
|
if !n.typ.isNil() {
|
|
return convErr
|
|
}
|
|
return nil
|
|
default:
|
|
return convErr
|
|
}
|
|
|
|
if err := check.representable(n, rtyp); err != nil {
|
|
return err
|
|
}
|
|
n.rval, err = check.convertConst(n.rval, rtyp)
|
|
if err != nil {
|
|
if errors.Is(err, errCantConvert) {
|
|
return convErr
|
|
}
|
|
return n.cfgErrorf(err.Error())
|
|
}
|
|
n.typ = ityp
|
|
return nil
|
|
}
|
|
|
|
func (check typecheck) representable(n *node, t reflect.Type) error {
|
|
if !n.rval.IsValid() {
|
|
// TODO(nick): This should be an error as the const is in the frame which is undesirable.
|
|
return nil
|
|
}
|
|
c, ok := n.rval.Interface().(constant.Value)
|
|
if !ok {
|
|
// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
|
|
return nil
|
|
}
|
|
|
|
if !representableConst(c, t) {
|
|
typ := n.typ.TypeOf()
|
|
if isNumber(typ) && isNumber(t) {
|
|
// numeric conversion : error msg
|
|
//
|
|
// integer -> integer : overflows
|
|
// integer -> float : overflows (actually not possible)
|
|
// float -> integer : truncated
|
|
// float -> float : overflows
|
|
//
|
|
if !isInt(typ) && isInt(t) {
|
|
return n.cfgErrorf("%s truncated to %s", c.ExactString(), t.Kind().String())
|
|
}
|
|
return n.cfgErrorf("%s overflows %s", c.ExactString(), t.Kind().String())
|
|
}
|
|
return n.cfgErrorf("cannot convert %s to %s", c.ExactString(), t.Kind().String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (check typecheck) convertConst(v reflect.Value, t reflect.Type) (reflect.Value, error) {
|
|
if !v.IsValid() {
|
|
// TODO(nick): This should be an error as the const is in the frame which is undesirable.
|
|
return v, nil
|
|
}
|
|
c, ok := v.Interface().(constant.Value)
|
|
if !ok {
|
|
// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
|
|
return v, nil
|
|
}
|
|
|
|
kind := t.Kind()
|
|
switch kind {
|
|
case reflect.Bool:
|
|
v = reflect.ValueOf(constant.BoolVal(c))
|
|
case reflect.String:
|
|
v = reflect.ValueOf(constant.StringVal(c))
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
i, _ := constant.Int64Val(constant.ToInt(c))
|
|
v = reflect.ValueOf(i).Convert(t)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
i, _ := constant.Uint64Val(constant.ToInt(c))
|
|
v = reflect.ValueOf(i).Convert(t)
|
|
case reflect.Float32:
|
|
f, _ := constant.Float32Val(constant.ToFloat(c))
|
|
v = reflect.ValueOf(f)
|
|
case reflect.Float64:
|
|
f, _ := constant.Float64Val(constant.ToFloat(c))
|
|
v = reflect.ValueOf(f)
|
|
case reflect.Complex64:
|
|
r, _ := constant.Float32Val(constant.Real(c))
|
|
i, _ := constant.Float32Val(constant.Imag(c))
|
|
v = reflect.ValueOf(complex(r, i)).Convert(t)
|
|
case reflect.Complex128:
|
|
r, _ := constant.Float64Val(constant.Real(c))
|
|
i, _ := constant.Float64Val(constant.Imag(c))
|
|
v = reflect.ValueOf(complex(r, i)).Convert(t)
|
|
default:
|
|
return v, errCantConvert
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
var bitlen = [...]int{
|
|
reflect.Int: 64,
|
|
reflect.Int8: 8,
|
|
reflect.Int16: 16,
|
|
reflect.Int32: 32,
|
|
reflect.Int64: 64,
|
|
reflect.Uint: 64,
|
|
reflect.Uint8: 8,
|
|
reflect.Uint16: 16,
|
|
reflect.Uint32: 32,
|
|
reflect.Uint64: 64,
|
|
reflect.Uintptr: 64,
|
|
}
|
|
|
|
func representableConst(c constant.Value, t reflect.Type) bool {
|
|
switch {
|
|
case isInt(t):
|
|
x := constant.ToInt(c)
|
|
if x.Kind() != constant.Int {
|
|
return false
|
|
}
|
|
switch t.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if _, ok := constant.Int64Val(x); !ok {
|
|
return false
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
if _, ok := constant.Uint64Val(x); !ok {
|
|
return false
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
return constant.BitLen(x) <= bitlen[t.Kind()]
|
|
case isFloat(t):
|
|
x := constant.ToFloat(c)
|
|
if x.Kind() != constant.Float {
|
|
return false
|
|
}
|
|
switch t.Kind() {
|
|
case reflect.Float32:
|
|
f, _ := constant.Float32Val(x)
|
|
return !math.IsInf(float64(f), 0)
|
|
case reflect.Float64:
|
|
f, _ := constant.Float64Val(x)
|
|
return !math.IsInf(f, 0)
|
|
default:
|
|
return false
|
|
}
|
|
case isComplex(t):
|
|
x := constant.ToComplex(c)
|
|
if x.Kind() != constant.Complex {
|
|
return false
|
|
}
|
|
switch t.Kind() {
|
|
case reflect.Complex64:
|
|
r, _ := constant.Float32Val(constant.Real(x))
|
|
i, _ := constant.Float32Val(constant.Imag(x))
|
|
return !math.IsInf(float64(r), 0) && !math.IsInf(float64(i), 0)
|
|
case reflect.Complex128:
|
|
r, _ := constant.Float64Val(constant.Real(x))
|
|
i, _ := constant.Float64Val(constant.Imag(x))
|
|
return !math.IsInf(r, 0) && !math.IsInf(i, 0)
|
|
default:
|
|
return false
|
|
}
|
|
case isString(t):
|
|
return c.Kind() == constant.String
|
|
case isBoolean(t):
|
|
return c.Kind() == constant.Bool
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isShiftAction(a action) bool {
|
|
switch a {
|
|
case aShl, aShr, aShlAssign, aShrAssign:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isComparisonAction(a action) bool {
|
|
switch a {
|
|
case aEqual, aNotEqual, aGreater, aGreaterEqual, aLower, aLowerEqual:
|
|
return true
|
|
}
|
|
return false
|
|
}
|