Commit ae8da3a2 authored by Robert Griesemer's avatar Robert Griesemer

go/types: len(((*T)(nil)).X) is const if X is an array

Fixes #4744.

R=adonovan
CC=golang-dev
https://golang.org/cl/7305080
parent d8e3b16f
...@@ -181,7 +181,7 @@ var tests = []string{ ...@@ -181,7 +181,7 @@ var tests = []string{
// "runtime", // "runtime",
"runtime/cgo", "runtime/cgo",
// "runtime/debug", // rejects a valid constant - issue 4744 "runtime/debug",
"runtime/pprof", "runtime/pprof",
"sort", "sort",
......
...@@ -72,7 +72,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota ...@@ -72,7 +72,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
case _Cap, _Len: case _Cap, _Len:
mode := invalid mode := invalid
var val interface{} var val interface{}
switch typ := implicitDeref(underlying(x.typ)).(type) { switch typ := implicitArrayDeref(underlying(x.typ)).(type) {
case *Basic: case *Basic:
if isString(typ) && id == _Len { if isString(typ) && id == _Len {
if x.mode == constant { if x.mode == constant {
...@@ -85,7 +85,11 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota ...@@ -85,7 +85,11 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
case *Array: case *Array:
mode = value 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 mode = constant
val = typ.Len val = typ.Len
} }
...@@ -382,10 +386,10 @@ Error: ...@@ -382,10 +386,10 @@ Error:
x.expr = call 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. // otherwise it returns typ.
// //
func implicitDeref(typ Type) Type { func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok { if p, ok := typ.(*Pointer); ok {
if a, ok := underlying(p.Base).(*Array); ok { if a, ok := underlying(p.Base).(*Array); ok {
return a return a
...@@ -394,25 +398,25 @@ func implicitDeref(typ Type) Type { ...@@ -394,25 +398,25 @@ func implicitDeref(typ Type) Type {
return typ return typ
} }
// containsCallsOrReceives returns true if the expression x contains // containsCallsOrReceives reports if x contains function calls or channel receives.
// function calls or channel receives; it returns false otherwise. // Expects that x was type-checked already.
// //
func containsCallsOrReceives(x ast.Expr) bool { func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) {
res := false
ast.Inspect(x, func(x ast.Node) bool { ast.Inspect(x, func(x ast.Node) bool {
switch x := x.(type) { switch x := x.(type) {
case *ast.CallExpr: case *ast.CallExpr:
res = true // calls and conversions look the same
return false if !check.conversions[x] {
found = true
}
case *ast.UnaryExpr: case *ast.UnaryExpr:
if x.Op == token.ARROW { if x.Op == token.ARROW {
res = true found = true
return false
} }
} }
return true return !found // no need to continue if found
}) })
return res return
} }
// unparen removes any parentheses surrounding an expression and returns // unparen removes any parentheses surrounding an expression and returns
......
...@@ -27,6 +27,7 @@ type checker struct { ...@@ -27,6 +27,7 @@ type checker struct {
objects map[*ast.Object]Object // maps *ast.Objects 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 initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
methods map[*TypeName]*Scope // maps type names to associated methods 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 funclist []function // list of functions/methods with correct signatures and non-empty bodies
funcsig *Signature // signature of currently typechecked function funcsig *Signature // signature of currently typechecked function
pos []token.Pos // stack of expr positions; debugging support, used if trace is set pos []token.Pos // stack of expr positions; debugging support, used if trace is set
...@@ -399,6 +400,7 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, ...@@ -399,6 +400,7 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package,
objects: make(map[*ast.Object]Object), objects: make(map[*ast.Object]Object),
initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec), initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
methods: make(map[*TypeName]*Scope), methods: make(map[*TypeName]*Scope),
conversions: make(map[*ast.CallExpr]bool),
} }
// handle panics // handle panics
......
...@@ -40,6 +40,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota ...@@ -40,6 +40,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
x.mode = value x.mode = value
} }
check.conversions[conv] = true // for cap/len checking
x.expr = conv x.expr = conv
x.typ = typ x.typ = typ
return return
......
...@@ -33,6 +33,10 @@ func _cap() { ...@@ -33,6 +33,10 @@ func _cap() {
assert(_4 == 20) assert(_4 == 20)
_5 := cap(c) _5 := cap(c)
cap /* ERROR "not used" */ (c) cap /* ERROR "not used" */ (c)
// issue 4744
type T struct{ a [10]int }
const _ = cap(((*T)(nil)).a)
} }
func _close() { func _close() {
...@@ -151,6 +155,10 @@ func _len() { ...@@ -151,6 +155,10 @@ func _len() {
var ch <-chan int var ch <-chan int
const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)]) const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)])
_ = nn // TODO(gri) remove this once unused constants get type-checked _ = 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() { func _make() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment