// index checks an index/size expression arg for validity. // If length >= 0, it is the upper bound for arg. // TODO(gri): Do we need iota? func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) { var x operand check.expr(&x, arg, nil, iota) // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index/size must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "%s must be integer", &x) return } // a constant index/size i must be 0 <= i < length if x.mode == constant { if exact.Sign(x.val) < 0 { check.invalidArg(x.pos(), "%s must not be negative", &x) return } i, ok = exact.Int64Val(x.val) if !ok || length >= 0 && i >= length { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < length ] return i, true } return -1, true }
// Int64 returns the numeric value of this literal truncated to fit // a signed 64-bit integer. // func (l *Literal) Int64() int64 { switch x := l.Value; x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i } return 0 case exact.Float: f, _ := exact.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected literal value: %T", l.Value)) }
func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool { switch x.Kind() { case exact.Unknown: return true case exact.Bool: return as == Bool || as == UntypedBool case exact.Int: if x, ok := exact.Int64Val(x); ok { switch as { case Int: var s = uint(ctxt.sizeof(Typ[as])) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64: return true case Uint, Uintptr: if s := uint(ctxt.sizeof(Typ[as])) * 8; s < 64 { return 0 <= x && x <= int64(1)<<s-1 } return 0 <= x case Uint8: const s = 8 return 0 <= x && x <= 1<<s-1 case Uint16: const s = 16 return 0 <= x && x <= 1<<s-1 case Uint32: const s = 32 return 0 <= x && x <= 1<<s-1 case Uint64: return 0 <= x case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedInt, UntypedFloat, UntypedComplex: return true } } n := exact.BitLen(x) switch as { case Uint, Uintptr: var s = uint(ctxt.sizeof(Typ[as])) * 8 return exact.Sign(x) >= 0 && n <= int(s) case Uint64: return exact.Sign(x) >= 0 && n <= 64 case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedInt, UntypedFloat, UntypedComplex: return true } case exact.Float: switch as { case Float32: return true // TODO(gri) fix this case Float64: return true // TODO(gri) fix this case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedFloat, UntypedComplex: return true } case exact.Complex: switch as { case Complex64: return true // TODO(gri) fix this case Complex128: return true // TODO(gri) fix this case UntypedComplex: return true } case exact.String: return as == String || as == UntypedString case exact.Nil: return as == UntypedNil || as == UnsafePointer default: unreachable() } return false }
// rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // iota >= 0 indicates that the expression is part of a constant declaration. // cycleOk indicates whether it is ok for a type expression to refer to itself. // func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) { if trace { c := "" if cycleOk { c = " тиБ" } check.trace(e.Pos(), "%s%s", e, c) defer check.untrace("=> %s", x) } // record final type of x if untyped, notify clients of type otherwise defer check.callExpr(x) switch e := e.(type) { case *ast.BadExpr: goto Error // error was reported before case *ast.Ident: if e.Name == "_" { check.invalidOp(e.Pos(), "cannot use _ as value or type") goto Error } obj := check.lookup(e) if obj == nil { goto Error // error was reported before } check.object(obj, cycleOk) switch obj := obj.(type) { case *Package: check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) goto Error case *Const: if obj.Type == Typ[Invalid] { goto Error } x.mode = constant if obj == universeIota { if iota < 0 { check.invalidAST(e.Pos(), "cannot use iota outside constant declaration") goto Error } x.val = exact.MakeInt64(int64(iota)) } else { x.val = obj.Val // may be nil if we don't know the constant value } case *TypeName: x.mode = typexpr if !cycleOk && underlying(obj.Type) == nil { check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) x.expr = e x.typ = Typ[Invalid] return // don't goto Error - need x.mode == typexpr } case *Var: x.mode = variable case *Func: x.mode = value default: unreachable() } x.typ = obj.GetType() case *ast.Ellipsis: // ellipses are handled explicitly where they are legal // (array composite literals and parameter lists) check.errorf(e.Pos(), "invalid use of '...'") goto Error case *ast.BasicLit: x.setConst(e.Kind, e.Value) if x.mode == invalid { check.invalidAST(e.Pos(), "invalid literal %v", e.Value) goto Error } case *ast.FuncLit: if sig, ok := check.typ(e.Type, false).(*Signature); ok { x.mode = value x.typ = sig check.later(nil, sig, e.Body) } else { check.invalidAST(e.Pos(), "invalid function literal %s", e) goto Error } case *ast.CompositeLit: typ := hint openArray := false if e.Type != nil { // [...]T array types may only appear with composite literals. // Check for them here so we don't have to handle ... in general. typ = nil if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { // We have an "open" [...]T array type. // Create a new ArrayType with unknown length (-1) // and finish setting it up after analyzing the literal. typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)} openArray = true } } if typ == nil { typ = check.typ(e.Type, false) } } if typ == nil { check.errorf(e.Pos(), "missing type in composite literal") goto Error } switch utyp := underlying(deref(typ)).(type) { case *Struct: if len(e.Elts) == 0 { break } fields := utyp.Fields if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*ast.Ident) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name}) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue } // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true check.expr(x, kv.Value, nil, iota) etyp := fields[i].Type if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } } else { // no element must have a key for i, e := range e.Elts { if kv, _ := e.(*ast.KeyValueExpr); kv != nil { check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") continue } check.expr(x, e, nil, iota) if i >= len(fields) { check.errorf(x.pos(), "too many values in struct literal") break // cannot continue } // i < len(fields) etyp := fields[i].Type if !check.assignment(x, etyp) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) } continue } } if len(e.Elts) < len(fields) { check.errorf(e.Rbrace, "too few values in struct literal") // ok to continue } } case *Array: n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota) // if we have an "open" [...]T array, set the length now that we know it if openArray { utyp.Len = n } case *Slice: check.indexedElts(e.Elts, utyp.Elt, -1, iota) case *Map: visited := make(map[interface{}]bool, len(e.Elts)) for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { check.errorf(e.Pos(), "missing key in map literal") continue } check.compositeLitKey(kv.Key) check.expr(x, kv.Key, nil, iota) if !check.assignment(x, utyp.Key) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key) } continue } if x.mode == constant { if visited[x.val] { check.errorf(x.pos(), "duplicate key %s in map literal", x.val) continue } visited[x.val] = true } check.expr(x, kv.Value, utyp.Elt, iota) if !check.assignment(x, utyp.Elt) { if x.mode != invalid { check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt) } continue } } default: check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) goto Error } x.mode = value x.typ = typ case *ast.ParenExpr: check.rawExpr(x, e.X, nil, iota, cycleOk) case *ast.SelectorExpr: sel := e.Sel.Name // If the identifier refers to a package, handle everything here // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { if pkg, ok := check.lookup(ident).(*Package); ok { exp := pkg.Scope.Lookup(sel) if exp == nil { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) goto Error } else if !ast.IsExported(exp.GetName()) { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) goto Error } check.register(e.Sel, exp) // Simplified version of the code for *ast.Idents: // - imported packages use types.Scope and types.Objects // - imported objects are always fully initialized switch exp := exp.(type) { case *Const: assert(exp.Val != nil) x.mode = constant x.typ = exp.Type x.val = exp.Val case *TypeName: x.mode = typexpr x.typ = exp.Type case *Var: x.mode = variable x.typ = exp.Type case *Func: x.mode = value x.typ = exp.Type default: unreachable() } x.expr = e return } } check.exprOrType(x, e.X, iota, false) if x.mode == invalid { goto Error } res := lookupField(x.typ, QualifiedName{check.pkg, sel}) if res.mode == invalid { check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) goto Error } if x.mode == typexpr { // method expression sig, ok := res.typ.(*Signature) if !ok { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } // the receiver type becomes the type of the first function // argument of the method expression's function type // TODO(gri) at the moment, method sets don't correctly track // pointer vs non-pointer receivers => typechecker is too lenient x.mode = value x.typ = &Signature{ Params: append([]*Var{{Type: x.typ}}, sig.Params...), Results: sig.Results, IsVariadic: sig.IsVariadic, } } else { // regular selector x.mode = res.mode x.typ = res.typ } case *ast.IndexExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := underlying(x.typ).(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) } // an indexed string always yields a byte value // (not a constant) even if the string and the // index are constant x.mode = value x.typ = Typ[Byte] } case *Array: valid = true length = typ.Len if x.mode != variable { x.mode = value } x.typ = typ.Elt case *Pointer: if typ, _ := underlying(typ.Base).(*Array); typ != nil { valid = true length = typ.Len x.mode = variable x.typ = typ.Elt } case *Slice: valid = true x.mode = variable x.typ = typ.Elt case *Map: var key operand check.expr(&key, e.Index, nil, iota) if !check.assignment(&key, typ.Key) { if key.mode != invalid { check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.Key) } goto Error } x.mode = valueok x.typ = typ.Elt x.expr = e return } if !valid { check.invalidOp(x.pos(), "cannot index %s", x) goto Error } if e.Index == nil { check.invalidAST(e.Pos(), "missing index expression for %s", x) return } check.index(e.Index, length, iota) // ok to continue case *ast.SliceExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } valid := false length := int64(-1) // valid if >= 0 switch typ := underlying(x.typ).(type) { case *Basic: if isString(typ) { valid = true if x.mode == constant { length = int64(len(exact.StringVal(x.val))) + 1 // +1 for slice } // a sliced string always yields a string value // of the same type as the original string (not // a constant) even if the string and the indices // are constant x.mode = value // x.typ doesn't change, but if it is an untyped // string it becomes string (see also issue 4913). if typ.Kind == UntypedString { x.typ = Typ[String] } } case *Array: valid = true length = typ.Len + 1 // +1 for slice if x.mode != variable { check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) goto Error } x.typ = &Slice{Elt: typ.Elt} case *Pointer: if typ, _ := underlying(typ.Base).(*Array); typ != nil { valid = true length = typ.Len + 1 // +1 for slice x.mode = variable x.typ = &Slice{Elt: typ.Elt} } case *Slice: valid = true x.mode = variable // x.typ doesn't change } if !valid { check.invalidOp(x.pos(), "cannot slice %s", x) goto Error } lo := int64(0) if e.Low != nil { if i, ok := check.index(e.Low, length, iota); ok && i >= 0 { lo = i } } hi := int64(-1) if e.High != nil { if i, ok := check.index(e.High, length, iota); ok && i >= 0 { hi = i } } else if length >= 0 { hi = length } if lo >= 0 && hi >= 0 && lo > hi { check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi) // ok to continue } case *ast.TypeAssertExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } var T *Interface if T, _ = underlying(x.typ).(*Interface); T == nil { check.invalidOp(x.pos(), "%s is not an interface", x) goto Error } // x.(type) expressions are handled explicitly in type switches if e.Type == nil { check.errorf(e.Pos(), "use of .(type) outside type switch") goto Error } typ := check.typ(e.Type, false) if typ == Typ[Invalid] { goto Error } if method, wrongType := missingMethod(typ, T); method != nil { var msg string if wrongType { msg = "%s cannot have dynamic type %s (wrong type for method %s)" } else { msg = "%s cannot have dynamic type %s (missing method %s)" } check.errorf(e.Type.Pos(), msg, x, typ, method.Name) // ok to continue } x.mode = valueok x.expr = e x.typ = typ case *ast.CallExpr: check.exprOrType(x, e.Fun, iota, false) if x.mode == invalid { goto Error } else if x.mode == typexpr { check.conversion(x, e, x.typ, iota) } else if sig, ok := underlying(x.typ).(*Signature); ok { // check parameters // If we have a trailing ... at the end of the parameter // list, the last argument must match the parameter type // []T of a variadic function parameter x ...T. passSlice := false if e.Ellipsis.IsValid() { if sig.IsVariadic { passSlice = true } else { check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) // ok to continue } } // If we have a single argument that is a function call // we need to handle it separately. Determine if this // is the case without checking the argument. var call *ast.CallExpr if len(e.Args) == 1 { call, _ = unparen(e.Args[0]).(*ast.CallExpr) } n := 0 // parameter count if call != nil { // We have a single argument that is a function call. check.expr(x, call, nil, -1) if x.mode == invalid { goto Error // TODO(gri): we can do better } if t, _ := x.typ.(*Result); t != nil { // multiple result values n = len(t.Values) for i, obj := range t.Values { x.mode = value x.expr = nil // TODO(gri) can we do better here? (for good error messages) x.typ = obj.Type check.argument(sig, i, nil, x, passSlice && i+1 == n) } } else { // single result value n = 1 check.argument(sig, 0, nil, x, passSlice) } } else { // We don't have a single argument or it is not a function call. n = len(e.Args) for i, arg := range e.Args { check.argument(sig, i, arg, x, passSlice && i+1 == n) } } // determine if we have enough arguments if sig.IsVariadic { // a variadic function accepts an "empty" // last argument: count one extra n++ } if n < len(sig.Params) { check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) // ok to continue } // determine result switch len(sig.Results) { case 0: x.mode = novalue case 1: x.mode = value x.typ = sig.Results[0].Type default: x.mode = value x.typ = &Result{Values: sig.Results} } } else if bin, ok := x.typ.(*builtin); ok { check.builtin(x, e, bin, iota) } else { check.invalidOp(x.pos(), "cannot call non-function %s", x) goto Error } case *ast.StarExpr: check.exprOrType(x, e.X, iota, true) switch x.mode { case invalid: goto Error case typexpr: x.typ = &Pointer{Base: x.typ} default: if typ, ok := underlying(x.typ).(*Pointer); ok { x.mode = variable x.typ = typ.Base } else { check.invalidOp(x.pos(), "cannot indirect %s", x) goto Error } } case *ast.UnaryExpr: check.expr(x, e.X, nil, iota) if x.mode == invalid { goto Error } check.unary(x, e.Op) if x.mode == invalid { goto Error } case *ast.BinaryExpr: check.binary(x, e.X, e.Y, e.Op, iota) if x.mode == invalid { goto Error } case *ast.KeyValueExpr: // key:value expressions are handled in composite literals check.invalidAST(e.Pos(), "no key:value expected") goto Error case *ast.ArrayType: if e.Len != nil { check.expr(x, e.Len, nil, iota) if x.mode == invalid { goto Error } if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", x) } goto Error } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", x) goto Error } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", x) goto Error } x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)} } else { x.typ = &Slice{Elt: check.typ(e.Elt, true)} } x.mode = typexpr case *ast.StructType: x.mode = typexpr x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)} case *ast.FuncType: params, isVariadic := check.collectParams(e.Params, true) results, _ := check.collectParams(e.Results, false) x.mode = typexpr x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} case *ast.InterfaceType: x.mode = typexpr x.typ = &Interface{Methods: check.collectMethods(e.Methods)} case *ast.MapType: x.mode = typexpr x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)} case *ast.ChanType: x.mode = typexpr x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)} default: if debug { check.dump("expr = %v (%T)", e, e) } unreachable() } // everything went well x.expr = e return Error: x.mode = invalid x.expr = e }
// object typechecks an object by assigning it a type. // func (check *checker) object(obj Object, cycleOk bool) { switch obj := obj.(type) { case *Package: // nothing to do case *Const: if obj.Type != nil { return // already checked } // The obj.Val field for constants is initialized to its respective // iota value (type int) by the parser. // If the object's type is Typ[Invalid], the object value is ignored. // If the object's type is valid, the object value must be a legal // constant value; it may be nil to indicate that we don't know the // value of the constant (e.g., in: "const x = float32("foo")" we // know that x is a constant and has type float32, but we don't // have a value due to the error in the conversion). if obj.visited { check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name) obj.Type = Typ[Invalid] return } obj.visited = true spec := obj.spec iota, ok := exact.Int64Val(obj.Val) assert(ok) obj.Val = exact.MakeUnknown() // determine spec for type and initialization expressions init := spec if len(init.Values) == 0 { init = check.initspecs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota)) case *Var: if obj.Type != nil { return // already checked } if obj.visited { check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name) obj.Type = Typ[Invalid] return } obj.visited = true switch d := obj.decl.(type) { case *ast.Field: unreachable() // function parameters are always typed when collected case *ast.ValueSpec: check.valueSpec(d.Pos(), obj, d.Names, d, 0) case *ast.AssignStmt: unreachable() // assign1to1 sets the type for failing short var decls default: unreachable() // see also function newObj } case *TypeName: if obj.Type != nil { return // already checked } typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk)) // typecheck associated method signatures if scope := check.methods[obj]; scope != nil { switch t := typ.Underlying.(type) { case *Struct: // struct fields must not conflict with methods for _, f := range t.Fields { if m := scope.Lookup(f.Name); m != nil { check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name) // ok to continue } } case *Interface: // methods cannot be associated with an interface type for _, m := range scope.Entries { recv := m.(*Func).decl.Recv.List[0].Type check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) // ok to continue } } // typecheck method signatures var methods []*Method for _, obj := range scope.Entries { m := obj.(*Func) sig := check.typ(m.decl.Type, cycleOk).(*Signature) params, _ := check.collectParams(m.decl.Recv, false) sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter m.Type = sig methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig}) check.later(m, sig, m.decl.Body) } typ.Methods = methods delete(check.methods, obj) // we don't need this scope anymore } case *Func: if obj.Type != nil { return // already checked } fdecl := obj.decl // methods are typechecked when their receivers are typechecked if fdecl.Recv == nil { sig := check.typ(fdecl.Type, cycleOk).(*Signature) if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue } obj.Type = sig check.later(obj, sig, fdecl.Body) } default: unreachable() } }