diff --git a/src/pkg/exp/gotype/gotype_test.go b/src/pkg/exp/gotype/gotype_test.go index d6ab5eae41c..67ab7cfa74a 100644 --- a/src/pkg/exp/gotype/gotype_test.go +++ b/src/pkg/exp/gotype/gotype_test.go @@ -181,7 +181,7 @@ var tests = []string{ // "runtime", "runtime/cgo", - // "runtime/debug", // rejects a valid constant - issue 4744 + "runtime/debug", "runtime/pprof", "sort", diff --git a/src/pkg/go/types/builtins.go b/src/pkg/go/types/builtins.go index 867f1e9c6de..7141856ccca 100644 --- a/src/pkg/go/types/builtins.go +++ b/src/pkg/go/types/builtins.go @@ -72,7 +72,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota 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 { @@ -85,7 +85,11 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota 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 } @@ -382,10 +386,10 @@ Error: 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 @@ -394,25 +398,25 @@ func implicitDeref(typ Type) Type { 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 diff --git a/src/pkg/go/types/check.go b/src/pkg/go/types/check.go index fb9d7573d57..e8af0af569d 100644 --- a/src/pkg/go/types/check.go +++ b/src/pkg/go/types/check.go @@ -21,15 +21,16 @@ type checker struct { 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) { @@ -392,13 +393,14 @@ type bailout struct{} 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 diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go index 65359f319d1..36661bea5f2 100644 --- a/src/pkg/go/types/conversions.go +++ b/src/pkg/go/types/conversions.go @@ -40,6 +40,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota x.mode = value } + check.conversions[conv] = true // for cap/len checking x.expr = conv x.typ = typ return diff --git a/src/pkg/go/types/testdata/builtins.src b/src/pkg/go/types/testdata/builtins.src index 6c848fc2776..241c2318763 100644 --- a/src/pkg/go/types/testdata/builtins.src +++ b/src/pkg/go/types/testdata/builtins.src @@ -33,6 +33,10 @@ func _cap() { assert(_4 == 20) _5 := cap(c) cap /* ERROR "not used" */ (c) + + // issue 4744 + type T struct{ a [10]int } + const _ = cap(((*T)(nil)).a) } func _close() { @@ -151,6 +155,10 @@ func _len() { var ch <-chan int const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)]) _ = nn // TODO(gri) remove this once unused constants get type-checked + + // issue 4744 + type T struct{ a [10]int } + const _ = len(((*T)(nil)).a) } func _make() {