From bd60de5995a9a214895bcac3e56d046d39d997d1 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 26 Jan 2021 11:12:04 +0100 Subject: [PATCH] interp: allow early constant evaluation from builtin call One builtin has been identified to be used for constant definition: len(), with a constant string argument. Add support for this. Fixes #1012. --- _test/const23.go | 12 ++++++++++++ _test/const24.go | 14 ++++++++++++++ interp/cfg.go | 34 ++++++++++++++++++++++++++++++---- interp/run.go | 15 +++++++++++++++ interp/type.go | 2 +- 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 _test/const23.go create mode 100644 _test/const24.go diff --git a/_test/const23.go b/_test/const23.go new file mode 100644 index 000000000..64602e8ed --- /dev/null +++ b/_test/const23.go @@ -0,0 +1,12 @@ +package main + +const maxlen = len("hello") + +var gfm = [maxlen]byte{} + +func main() { + println(len(gfm)) +} + +// Output: +// 5 diff --git a/_test/const24.go b/_test/const24.go new file mode 100644 index 000000000..601a42154 --- /dev/null +++ b/_test/const24.go @@ -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 diff --git a/interp/cfg.go b/interp/cfg.go index 9f82f22e1..afed70535 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -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. @@ -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 } @@ -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): @@ -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 == "_" { diff --git a/interp/run.go b/interp/run.go index 3e4050b5f..f80e50917 100644 --- a/interp/run.go +++ b/interp/run.go @@ -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]) diff --git a/interp/type.go b/interp/type.go index 2cd2eff00..ff9c87ae6 100644 --- a/interp/type.go +++ b/interp/type.go @@ -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 }