Skip to content

Commit

Permalink
interp: allow early constant evaluation from builtin call
Browse files Browse the repository at this point in the history
One builtin has been identified to be used for constant definition:
len(), with a constant string argument. Add support for this.

Fixes #1012.
  • Loading branch information
mvertes authored Jan 26, 2021
1 parent 274eecd commit bd60de5
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 5 deletions.
12 changes: 12 additions & 0 deletions _test/const23.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

const maxlen = len("hello")

var gfm = [maxlen]byte{}

func main() {
println(len(gfm))
}

// Output:
// 5
14 changes: 14 additions & 0 deletions _test/const24.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

var aa = [...]int{1, 2, 3}

const maxlen = cap(aa)

var gfm = [maxlen]byte{}

func main() {
println(len(gfm))
}

// Output:
// 3
34 changes: 30 additions & 4 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
switch {
case n.action != aAssign:
// Do not optimize assign combined with another operator.
case src.rval.IsValid():
// Do not skip assign operation when setting from a constant value.
case isMapEntry(dest):
// Setting a map entry needs an additional step, do not optimize.
// As we only write, skip the default useless getIndexMap dest action.
Expand Down Expand Up @@ -855,13 +857,15 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
wireChild(n)
switch {
case interp.isBuiltinCall(n):
err = check.builtin(n.child[0].ident, n, n.child[1:], n.action == aCallSlice)
c0 := n.child[0]
bname := c0.ident
err = check.builtin(bname, n, n.child[1:], n.action == aCallSlice)
if err != nil {
break
}

n.gen = n.child[0].sym.builtin
n.child[0].typ = &itype{cat: builtinT}
n.gen = c0.sym.builtin
c0.typ = &itype{cat: builtinT}
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
Expand All @@ -872,10 +876,18 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case n.anc.kind == returnStmt:
// Store result directly to frame output location, to avoid a frame copy.
n.findex = 0
case bname == "cap" && isInConstOrTypeDecl(n):
capConst(n)
n.findex = notInFrame
n.gen = nop
case bname == "len" && isInConstOrTypeDecl(n):
lenConst(n)
n.findex = notInFrame
n.gen = nop
default:
n.findex = sc.add(n.typ)
}
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
if op, ok := constBltn[bname]; ok && n.anc.action != aAssign {
op(n) // pre-compute non-assigned constant :
}
case n.child[0].isType(sc):
Expand Down Expand Up @@ -2326,6 +2338,20 @@ func isRecursiveField(n *node) bool {
return false
}

func isInConstOrTypeDecl(n *node) bool {
anc := n.anc
for anc != nil {
switch anc.kind {
case constDecl, typeDecl:
return true
case varDecl, funcDecl:
return false
}
anc = anc.anc
}
return false
}

// isNewDefine returns true if node refers to a new definition.
func isNewDefine(n *node, sc *scope) bool {
if n.ident == "_" {
Expand Down
15 changes: 15 additions & 0 deletions interp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2847,6 +2847,21 @@ func _delete(n *node) {
})
}

func capConst(n *node) {
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
// There is no Cap() method for reflect.Type, just return Len() instead.
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
}

func lenConst(n *node) {
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
if c1 := n.child[1]; c1.rval.IsValid() {
n.rval.SetInt(int64(len(vString(c1.rval))))
} else {
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
}
}

func _len(n *node) {
dest := genValueOutput(n, reflect.TypeOf(int(0)))
value := genValue(n.child[1])
Expand Down
2 changes: 1 addition & 1 deletion interp/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if sym.kind != constSym {
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
}
if sym.typ == nil || sym.typ.cat != intT {
if sym.typ == nil || sym.typ.cat != intT || !sym.rval.IsValid() {
t.incomplete = true
break
}
Expand Down

0 comments on commit bd60de5

Please sign in to comment.