]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: len(((*T)(nil)).X) is const if X is an array
authorRobert Griesemer <gri@golang.org>
Tue, 12 Feb 2013 06:39:55 +0000 (22:39 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 12 Feb 2013 06:39:55 +0000 (22:39 -0800)
Fixes #4744.

R=adonovan
CC=golang-dev
https://golang.org/cl/7305080

src/pkg/exp/gotype/gotype_test.go
src/pkg/go/types/builtins.go
src/pkg/go/types/check.go
src/pkg/go/types/conversions.go
src/pkg/go/types/testdata/builtins.src

index d6ab5eae41c87a4afc8a99d851d6e4ed96af49c1..67ab7cfa74a51dceb5d9095704d92cd5c195f208 100644 (file)
@@ -181,7 +181,7 @@ var tests = []string{
 
        // "runtime",
        "runtime/cgo",
-       // "runtime/debug", // rejects a valid constant - issue 4744
+       "runtime/debug",
        "runtime/pprof",
 
        "sort",
index 867f1e9c6de0545f581dd81830c2cb1974db5907..7141856cccaab2e4ad64e221bd75a0beca94d355 100644 (file)
@@ -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
index fb9d7573d5792147e1a47bf959fe26277e6b8936..e8af0af569d4081214808dfd745fc48cbdc83c31 100644 (file)
@@ -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
index 65359f319d1ed9edf6944e8b48dd830659518313..36661bea5f2be8771f2268c07fd963c31fc5b76d 100644 (file)
@@ -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
index 6c848fc27766f7cc889cce186657d3006b3fc9dc..241c2318763f2622a6f45991849ed58fcac5bab1 100644 (file)
@@ -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() {