* fix: untyped constant cconverson to default type In definition assign expression, the source type is propagated to the assigned value. If the source is an untyped constant, the destination type must be set to the default type of the constant definition. A fixType function is provided to perform this. In addition, the type conversion and check of constants is refactored for simplifications. Fixes #727. * test: fix _test/const14.go
210 lines
6.1 KiB
Go
210 lines
6.1 KiB
Go
package interp
|
|
|
|
import (
|
|
"log"
|
|
"reflect"
|
|
"strconv"
|
|
)
|
|
|
|
// A sKind represents the kind of symbol.
|
|
type sKind uint
|
|
|
|
// Symbol kinds for the Go interpreter.
|
|
const (
|
|
undefSym sKind = iota
|
|
binSym // Binary from runtime
|
|
bltnSym // Builtin
|
|
constSym // Constant
|
|
funcSym // Function
|
|
labelSym // Label
|
|
pkgSym // Package
|
|
typeSym // Type
|
|
varSym // Variable
|
|
)
|
|
|
|
var symKinds = [...]string{
|
|
undefSym: "undefSym",
|
|
binSym: "binSym",
|
|
bltnSym: "bltnSym",
|
|
constSym: "constSym",
|
|
funcSym: "funcSym",
|
|
labelSym: "labelSym",
|
|
pkgSym: "pkgSym",
|
|
typeSym: "typeSym",
|
|
varSym: "varSym",
|
|
}
|
|
|
|
func (k sKind) String() string {
|
|
if k < sKind(len(symKinds)) {
|
|
return symKinds[k]
|
|
}
|
|
return "SymKind(" + strconv.Itoa(int(k)) + ")"
|
|
}
|
|
|
|
// A symbol represents an interpreter object such as type, constant, var, func,
|
|
// label, builtin or binary object. Symbols are defined within a scope.
|
|
type symbol struct {
|
|
kind sKind
|
|
typ *itype // Type of value
|
|
node *node // Node value if index is negative
|
|
from []*node // list of nodes jumping to node if kind is label, or nil
|
|
recv *receiver // receiver node value, if sym refers to a method
|
|
index int // index of value in frame or -1
|
|
rval reflect.Value // default value (used for constants)
|
|
builtin bltnGenerator // Builtin function or nil
|
|
global bool // true if symbol is defined in global space
|
|
// TODO: implement constant checking
|
|
//constant bool // true if symbol value is constant
|
|
}
|
|
|
|
// scope type stores symbols in maps, and frame layout as array of types
|
|
// The purposes of scopes are to manage the visibility of each symbol
|
|
// and to store the memory frame layout information (type and index in frame)
|
|
// at each level (global, package, functions)
|
|
//
|
|
// scopes are organized in a stack fashion: a first scope (universe) is created
|
|
// once at global level, and for each block (package, func, for, etc...), a new
|
|
// scope is pushed at entry, and poped at exit.
|
|
//
|
|
// Nested scopes with the same level value use the same frame: it allows to have
|
|
// exactly one frame per function, with a fixed position for each variable (named
|
|
// or not), no matter the inner complexity (number of nested blocks in the function)
|
|
//
|
|
// In symbols, the index value corresponds to the index in scope.types, and at
|
|
// execution to the index in frame, created exactly from the types layout.
|
|
//
|
|
type scope struct {
|
|
anc *scope // Ancestor upper scope
|
|
def *node // function definition node this scope belongs to, or nil
|
|
loop *node // loop exit node for break statement
|
|
loopRestart *node // loop restart node for continue statement
|
|
pkgID string // unique id of package in which scope is defined
|
|
types []reflect.Type // Frame layout, may be shared by same level scopes
|
|
level int // Frame level: number of frame indirections to access var during execution
|
|
sym map[string]*symbol // Map of symbols defined in this current scope
|
|
global bool // true if scope refers to global space (single frame for universe and package level scopes)
|
|
iota int // iota value in this scope
|
|
}
|
|
|
|
// push creates a new scope and chain it to the current one.
|
|
func (s *scope) push(indirect bool) *scope {
|
|
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
|
|
if indirect {
|
|
sc.types = []reflect.Type{}
|
|
sc.level = s.level + 1
|
|
} else {
|
|
// propagate size, types, def and global as scopes at same level share the same frame
|
|
sc.types = s.types
|
|
sc.def = s.def
|
|
sc.global = s.global
|
|
sc.level = s.level
|
|
}
|
|
// inherit loop state and pkgID from ancestor
|
|
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
|
|
return &sc
|
|
}
|
|
|
|
func (s *scope) pushBloc() *scope { return s.push(false) }
|
|
func (s *scope) pushFunc() *scope { return s.push(true) }
|
|
|
|
func (s *scope) pop() *scope {
|
|
if s.level == s.anc.level {
|
|
// propagate size and types, as scopes at same level share the same frame
|
|
s.anc.types = s.types
|
|
}
|
|
return s.anc
|
|
}
|
|
|
|
// lookup searches for a symbol in the current scope, and upper ones if not found
|
|
// it returns the symbol, the number of indirections level from the current scope
|
|
// and status (false if no result).
|
|
func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
|
level := s.level
|
|
for {
|
|
if sym, ok := s.sym[ident]; ok {
|
|
return sym, level - s.level, true
|
|
}
|
|
if s.anc == nil {
|
|
break
|
|
}
|
|
s = s.anc
|
|
}
|
|
return nil, 0, false
|
|
}
|
|
|
|
func (s *scope) rangeChanType(n *node) *itype {
|
|
if sym, _, found := s.lookup(n.child[1].ident); found {
|
|
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
|
|
return t
|
|
}
|
|
}
|
|
|
|
c := n.child[1]
|
|
if c.typ == nil {
|
|
return nil
|
|
}
|
|
switch {
|
|
case c.typ.cat == chanT, c.typ.cat == chanRecvT:
|
|
return c.typ
|
|
case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
|
|
return &itype{cat: chanT, val: &itype{cat: valueT, rtype: c.typ.rtype.Elem()}}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// fixType returns the input type, or a valid default type for untyped constant.
|
|
func (s *scope) fixType(t *itype) *itype {
|
|
if !t.untyped || t.cat != valueT {
|
|
return t
|
|
}
|
|
switch typ := t.TypeOf(); typ.Kind() {
|
|
case reflect.Int64:
|
|
return s.getType("int")
|
|
case reflect.Uint64:
|
|
return s.getType("uint")
|
|
case reflect.Float64:
|
|
return s.getType("float64")
|
|
case reflect.Complex128:
|
|
return s.getType("complex128")
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (s *scope) getType(ident string) *itype {
|
|
var t *itype
|
|
if sym, _, found := s.lookup(ident); found {
|
|
if sym.kind == typeSym {
|
|
t = sym.typ
|
|
}
|
|
}
|
|
return t
|
|
}
|
|
|
|
// add adds a type to the scope types array, and returns its index.
|
|
func (s *scope) add(typ *itype) (index int) {
|
|
if typ == nil {
|
|
log.Panic("nil type")
|
|
}
|
|
index = len(s.types)
|
|
t := typ.frameType()
|
|
if t == nil {
|
|
log.Panic("nil reflect type")
|
|
}
|
|
s.types = append(s.types, t)
|
|
return
|
|
}
|
|
|
|
func (interp *Interpreter) initScopePkg(pkgID string) *scope {
|
|
sc := interp.universe
|
|
|
|
interp.mutex.Lock()
|
|
if _, ok := interp.scopes[pkgID]; !ok {
|
|
interp.scopes[pkgID] = sc.pushBloc()
|
|
}
|
|
sc = interp.scopes[pkgID]
|
|
sc.pkgID = pkgID
|
|
interp.mutex.Unlock()
|
|
return sc
|
|
}
|