tc_checker.mx raw

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