checker.go raw

   1  package typecheck
   2  
   3  import (
   4  	"fmt"
   5  	"go/constant"
   6  	"moxie/syntax"
   7  )
   8  
   9  // Importer resolves import paths to Packages.
  10  type Importer interface {
  11  	Import(path string) (*Package, error)
  12  }
  13  
  14  // Config controls type checker behavior.
  15  type Config struct {
  16  	Importer Importer
  17  	Error    func(err error) // if nil, first error is returned and checking stops
  18  }
  19  
  20  // Checker is the Moxie type checker.
  21  // Create one with NewChecker, then call Files to type-check a package.
  22  type Checker struct {
  23  	conf         *Config
  24  	pkg          *Package
  25  	info         *Info
  26  	files        []*syntax.File
  27  	iota         int64  // current iota value
  28  	errors       []error
  29  	localScope   *Scope // innermost scope during function body checking; nil at pkg level
  30  }
  31  
  32  // NewChecker creates a checker for pkg. info receives the type information.
  33  func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
  34  	return &Checker{conf: conf, pkg: pkg, info: info}
  35  }
  36  
  37  // Check type-checks the given files as package pkg.
  38  // It returns an error if type checking fails.
  39  func Check(path string, files []*syntax.File, importer Importer) (*Package, *Info, error) {
  40  	pkg := NewPackage(path, packageName(files))
  41  	info := newInfo()
  42  	conf := &Config{Importer: importer}
  43  	c := NewChecker(conf, pkg, info)
  44  	if err := c.Files(files); err != nil {
  45  		return pkg, info, err
  46  	}
  47  	return pkg, info, nil
  48  }
  49  
  50  // packageName returns the package name declared in the first file, or "main".
  51  func packageName(files []*syntax.File) string {
  52  	for _, f := range files {
  53  		if f.PkgName != nil {
  54  			return f.PkgName.Value
  55  		}
  56  	}
  57  	return "main"
  58  }
  59  
  60  // Files type-checks the given files as the checker's package.
  61  func (c *Checker) Files(files []*syntax.File) error {
  62  	c.files = files
  63  
  64  	// Pass 1: collect all package-level declarations into pkg.scope.
  65  	for _, f := range files {
  66  		c.collectDecls(f)
  67  	}
  68  
  69  	// Pass 2a: resolve named types first (TypeDecl only) so that method
  70  	// registration in 2b can always find the Named type regardless of
  71  	// declaration order.
  72  	for _, f := range files {
  73  		for _, decl := range f.DeclList {
  74  			if d, ok := decl.(*syntax.TypeDecl); ok {
  75  				c.checkTypeDecl(d)
  76  			}
  77  		}
  78  	}
  79  
  80  	// Pass 2b: resolve vars and functions (including method registration).
  81  	for _, f := range files {
  82  		for _, decl := range f.DeclList {
  83  			switch decl.(type) {
  84  			case *syntax.TypeDecl:
  85  				// already done
  86  			default:
  87  				c.checkDecl(decl)
  88  			}
  89  		}
  90  	}
  91  
  92  	// Pass 3: check function bodies.
  93  	for _, f := range files {
  94  		for _, decl := range f.DeclList {
  95  			if fd, ok := decl.(*syntax.FuncDecl); ok {
  96  				c.checkFuncBody(fd)
  97  			}
  98  		}
  99  	}
 100  
 101  	if len(c.errors) > 0 {
 102  		return c.errors[0]
 103  	}
 104  	return nil
 105  }
 106  
 107  // error records a type error at pos.
 108  func (c *Checker) errorf(pos syntax.Pos, format string, args ...interface{}) {
 109  	err := &TypeError{Pos: pos, Msg: fmt.Sprintf(format, args...)}
 110  	if c.conf.Error != nil {
 111  		c.conf.Error(err)
 112  		c.errors = append(c.errors, err)
 113  	} else {
 114  		c.errors = append(c.errors, err)
 115  	}
 116  }
 117  
 118  // TypeError is a type-checking error with position info.
 119  type TypeError struct {
 120  	Pos syntax.Pos
 121  	Msg string
 122  }
 123  
 124  func (e *TypeError) Error() string {
 125  	return fmt.Sprintf("%s: %s", e.Pos, e.Msg)
 126  }
 127  
 128  // record stores type info for an expression.
 129  func (c *Checker) record(e syntax.Expr, tv TypeAndValue) {
 130  	if c.info != nil {
 131  		c.info.Types[e] = tv
 132  	}
 133  	stv := syntax.TypeAndValue{Type: tv.Type}
 134  	if tv.Value != nil {
 135  		if cv, ok := tv.Value.(constant.Value); ok {
 136  			stv.Value = cv
 137  		}
 138  	}
 139  	e.SetTypeInfo(stv)
 140  }
 141  
 142  // openScope creates a child scope of s and records it for node n.
 143  func (c *Checker) openScope(n syntax.Node, parent *Scope) *Scope {
 144  	s := NewScope(parent)
 145  	if c.info != nil {
 146  		c.info.Scopes[n] = s
 147  	}
 148  	return s
 149  }
 150  
 151  // lookup finds a named object starting from s, then universe.
 152  func (c *Checker) lookup(name string, s *Scope) (*Scope, Object) {
 153  	if found, obj := s.LookupParent(name); obj != nil {
 154  		return found, obj
 155  	}
 156  	if obj := Universe.Lookup(name); obj != nil {
 157  		return Universe, obj
 158  	}
 159  	return nil, nil
 160  }
 161  
 162  // lookupType finds a type name in localScope (if set), then package scope, then universe.
 163  // Used by resolveTypeName to find locally-defined types (e.g. inside function bodies).
 164  func (c *Checker) lookupType(name string) (*Scope, Object) {
 165  	if c.localScope != nil {
 166  		if sc, obj := c.localScope.LookupParent(name); obj != nil {
 167  			return sc, obj
 168  		}
 169  	}
 170  	return c.lookup(name, c.pkg.scope)
 171  }
 172