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.
This commit is contained in:
Marc Vertes
2021-01-26 11:12:04 +01:00
committed by GitHub
parent 274eecdf18
commit bd60de5995
5 changed files with 72 additions and 5 deletions

12
_test/const23.go Normal file
View File

@@ -0,0 +1,12 @@
package main
const maxlen = len("hello")
var gfm = [maxlen]byte{}
func main() {
println(len(gfm))
}
// Output:
// 5

14
_test/const24.go Normal file
View File

@@ -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

View File

@@ -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 == "_" {

View File

@@ -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])

View File

@@ -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
}