mirror of
https://github.com/golang/go
synced 2024-11-23 05:10:09 -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",
|
||||||
"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
|
|||||||
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
|
|||||||
|
|
||||||
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:
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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,
|
|||||||
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
|
|||||||
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
|
||||||
|
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)
|
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() {
|
|||||||
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() {
|
||||||
|
Loading…
Reference in New Issue
Block a user