case _Cap, _Len:
mode := invalid
var val interface{}
- switch typ := implicitDeref(underlying(x.typ)).(type) {
+ switch typ := implicitArrayDeref(underlying(x.typ)).(type) {
case *Basic:
if isString(typ) && id == _Len {
if x.mode == constant {
case *Array:
mode = value
- if !containsCallsOrReceives(arg0) {
+ // spec: "The expressions len(s) and cap(s) are constants
+ // if the type of s is an array or pointer to an array and
+ // the expression s does not contain channel receives or
+ // function calls; in this case s is not evaluated."
+ if !check.containsCallsOrReceives(arg0) {
mode = constant
val = typ.Len
}
x.expr = call
}
-// implicitDeref returns A if typ is of the form *A and A is an array;
+// implicitArrayDeref returns A if typ is of the form *A and A is an array;
// otherwise it returns typ.
//
-func implicitDeref(typ Type) Type {
+func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
if a, ok := underlying(p.Base).(*Array); ok {
return a
return typ
}
-// containsCallsOrReceives returns true if the expression x contains
-// function calls or channel receives; it returns false otherwise.
+// containsCallsOrReceives reports if x contains function calls or channel receives.
+// Expects that x was type-checked already.
//
-func containsCallsOrReceives(x ast.Expr) bool {
- res := false
+func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) {
ast.Inspect(x, func(x ast.Node) bool {
switch x := x.(type) {
case *ast.CallExpr:
- res = true
- return false
+ // calls and conversions look the same
+ if !check.conversions[x] {
+ found = true
+ }
case *ast.UnaryExpr:
if x.Op == token.ARROW {
- res = true
- return false
+ found = true
}
}
- return true
+ return !found // no need to continue if found
})
- return res
+ return
}
// unparen removes any parentheses surrounding an expression and returns
files []*ast.File
// lazily initialized
- pkg *Package // current package
- firsterr error // first error encountered
- idents map[*ast.Ident]Object // maps identifiers to their unique object
- objects map[*ast.Object]Object // maps *ast.Objects to their unique object
- initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
- methods map[*TypeName]*Scope // maps type names to associated methods
- funclist []function // list of functions/methods with correct signatures and non-empty bodies
- funcsig *Signature // signature of currently typechecked function
- pos []token.Pos // stack of expr positions; debugging support, used if trace is set
+ pkg *Package // current package
+ firsterr error // first error encountered
+ idents map[*ast.Ident]Object // maps identifiers to their unique object
+ objects map[*ast.Object]Object // maps *ast.Objects to their unique object
+ initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
+ methods map[*TypeName]*Scope // maps type names to associated methods
+ conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
+ funclist []function // list of functions/methods with correct signatures and non-empty bodies
+ funcsig *Signature // signature of currently typechecked function
+ pos []token.Pos // stack of expr positions; debugging support, used if trace is set
}
func (check *checker) register(id *ast.Ident, obj Object) {
func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
// initialize checker
check := checker{
- ctxt: ctxt,
- fset: fset,
- files: files,
- idents: make(map[*ast.Ident]Object),
- objects: make(map[*ast.Object]Object),
- initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
- methods: make(map[*TypeName]*Scope),
+ ctxt: ctxt,
+ fset: fset,
+ files: files,
+ idents: make(map[*ast.Ident]Object),
+ objects: make(map[*ast.Object]Object),
+ initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
+ methods: make(map[*TypeName]*Scope),
+ conversions: make(map[*ast.CallExpr]bool),
}
// handle panics