Files
moxa/interp/typecheck.go
mpl 16f5586a11 interp: apply integer division when appropriate
When working with an untyped const expression involving a division, if
the default type of the result should be an int (for example because the
default types of all the operands are ints as well), then we should make
sure that the operation that is applied is indeed an integer division,
and that the type of the result is not a float.

This is achieved by using the QUO_ASSIGN operator, instead of the QUO
operator.

This should fix several problems lurking around, and it notably fixes
one of the visible consequences, which is a systematic panic when using
the REPL as a "calculator".

This incidentally also allows us to revert what was done in
5dfc3b86dc since it now turns out it was
just a hack to fix one of the symptoms.

Fixes #864
2020-10-05 10:50:03 +02:00

1221 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 indexExpr:
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", c0.typ.id())
}
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 {
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)
}
_ = 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
}
switch n.action {
case aQuo, aRem:
if (c0.typ.untyped || isInt(t0)) && c1.typ.untyped && constant.Sign(c1.rval.Interface().(constant.Value)) == 0 {
return n.cfgErrorf("invalid operation: division by zero")
}
}
return nil
}
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 != 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 {
if isInterface(typ) || c != nil && !isConstType(typ) {
typ = n.typ.defaultType()
}
if err := check.convertUntyped(n, typ); err != nil {
return err
}
}
return nil
}
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
}