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