mirror of
https://github.com/golang/go
synced 2024-11-26 18:26:48 -07:00
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
This commit is contained in:
parent
d8e3b16f8b
commit
ae8da3a28c
@ -181,7 +181,7 @@ var tests = []string{
|
||||
|
||||
// "runtime",
|
||||
"runtime/cgo",
|
||||
// "runtime/debug", // rejects a valid constant - issue 4744
|
||||
"runtime/debug",
|
||||
"runtime/pprof",
|
||||
|
||||
"sort",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
8
src/pkg/go/types/testdata/builtins.src
vendored
8
src/pkg/go/types/testdata/builtins.src
vendored
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user