decl.go raw
1 package typecheck
2
3 import "moxie/syntax"
4
5 // collectDecls walks a file's top-level declarations and inserts all
6 // package-level names into pkg.scope. No type resolution yet — just
7 // name registration so forward references work during pass 2.
8 // Const groups are tracked so iota can be assigned correctly in pass 2.
9 func (c *Checker) collectDecls(f *syntax.File) {
10 // Track const groups: group pointer → ordered list of ConstDecls.
11 constGroups := map[*syntax.Group][]*syntax.ConstDecl{}
12 var constGroupOrder []*syntax.Group // preserve declaration order
13
14 for _, d := range f.DeclList {
15 switch d := d.(type) {
16 case *syntax.ImportDecl:
17 c.collectImport(d)
18 case *syntax.ConstDecl:
19 for _, name := range d.NameList {
20 obj := NewConst(c.pkg, name.Value, nil, nil)
21 c.declare(c.pkg.scope, name, obj)
22 }
23 // Group nil means a standalone const (iota=0).
24 g := d.Group
25 if g == nil {
26 // Use a unique sentinel group for standalone consts.
27 g = &syntax.Group{}
28 }
29 if _, seen := constGroups[g]; !seen {
30 constGroupOrder = append(constGroupOrder, g)
31 }
32 constGroups[g] = append(constGroups[g], d)
33 case *syntax.TypeDecl:
34 obj := NewTypeName(c.pkg, d.Name.Value, nil)
35 c.declare(c.pkg.scope, d.Name, obj)
36 case *syntax.VarDecl:
37 for _, name := range d.NameList {
38 obj := NewVar(c.pkg, name.Value, nil)
39 c.declare(c.pkg.scope, name, obj)
40 }
41 case *syntax.FuncDecl:
42 if d.Recv == nil {
43 // init functions: multiple allowed per package, never called by name.
44 if d.Name.Value == "init" {
45 continue
46 }
47 obj := NewFunc(c.pkg, d.Name.Value, nil)
48 c.declare(c.pkg.scope, d.Name, obj)
49 }
50 }
51 }
52
53 // Resolve const groups now that all names are registered.
54 for _, g := range constGroupOrder {
55 c.checkConstGroup(constGroups[g])
56 }
57 }
58
59 // collectImport resolves an import declaration and adds the package name
60 // to the file scope. (File scope is per-file; we approximate it with
61 // pkg.scope for now — proper file scopes come in the full implementation.)
62 func (c *Checker) collectImport(d *syntax.ImportDecl) {
63 if d.Path == nil {
64 return
65 }
66 path := d.Path.Value
67 if len(path) >= 2 {
68 path = path[1 : len(path)-1] // strip quotes
69 }
70 if c.conf.Importer == nil {
71 return
72 }
73 imported, err := c.conf.Importer.Import(path)
74 if err != nil {
75 c.errorf(d.Pos(), "cannot import %q: %v", path, err)
76 return
77 }
78 localName := imported.name
79 if d.LocalPkgName != nil {
80 localName = d.LocalPkgName.Value
81 }
82 if localName == "_" {
83 return
84 }
85 pkgObj := NewPkgName(c.pkg, localName, imported)
86 // File-level imports go into pkg.scope for now (proper file scopes in B3).
87 // Silently skip if the same name is already declared — multi-file packages
88 // often repeat the same import alias in each file (e.g. errorspkg "errors").
89 if existing := c.pkg.scope.Lookup(localName); existing != nil {
90 if ep, ok := existing.(*PkgName); ok && ep.imported == imported {
91 return // same import, already registered
92 }
93 }
94 if d.LocalPkgName != nil {
95 c.declare(c.pkg.scope, d.LocalPkgName, pkgObj)
96 } else {
97 c.pkg.scope.Insert(pkgObj)
98 }
99 }
100
101 // checkDecl resolves the type of a top-level declaration.
102 // Const decls are handled in collectDecls via checkConstGroup.
103 func (c *Checker) checkDecl(d syntax.Decl) {
104 switch d := d.(type) {
105 case *syntax.TypeDecl:
106 c.checkTypeDecl(d)
107 case *syntax.VarDecl:
108 c.checkVarDecl(d)
109 case *syntax.FuncDecl:
110 c.checkFuncDecl(d)
111 }
112 }
113
114 func (c *Checker) checkTypeDecl(d *syntax.TypeDecl) {
115 obj, ok := c.pkg.scope.Lookup(d.Name.Value).(*TypeName)
116 if !ok {
117 return
118 }
119 // Generic type declaration: register type params in a temporary scope so
120 // the underlying type can reference them (e.g. type Seq[V any] func(...V...)).
121 if len(d.TParamList) > 0 {
122 tmpScope := NewScope(c.pkg.scope)
123 for _, tp := range d.TParamList {
124 if tp.Name != nil && tp.Name.Value != "" {
125 tpObj := NewTypeName(c.pkg, tp.Name.Value,
126 NewTypeParam(NewTypeName(c.pkg, tp.Name.Value, nil), nil))
127 tmpScope.Insert(tpObj)
128 }
129 }
130 saved := c.localScope
131 c.localScope = tmpScope
132 defer func() { c.localScope = saved }()
133 }
134 underlying := c.resolveTypeExpr(d.Type)
135 named := NewNamed(obj, underlying)
136 obj.typ = named
137 }
138
139 func (c *Checker) checkVarDecl(d *syntax.VarDecl) {
140 var typ Type
141 if d.Type != nil {
142 typ = c.resolveTypeExpr(d.Type)
143 }
144 for _, name := range d.NameList {
145 if obj, ok := c.pkg.scope.Lookup(name.Value).(*Var); ok {
146 obj.typ = typ
147 }
148 }
149 }
150
151 func (c *Checker) checkFuncDecl(d *syntax.FuncDecl) {
152 // Build a temporary scope containing all type parameters visible in this
153 // function declaration: (1) explicit type params on the function itself,
154 // (2) type args from a generic receiver (func (h Handle[T]) ...).
155 typeParams := collectFuncTypeParams(d)
156 if len(typeParams) > 0 {
157 tmpScope := NewScope(c.pkg.scope)
158 for _, name := range typeParams {
159 obj := NewTypeName(c.pkg, name, NewTypeParam(NewTypeName(c.pkg, name, nil), nil))
160 tmpScope.Insert(obj)
161 }
162 saved := c.localScope
163 c.localScope = tmpScope
164 defer func() { c.localScope = saved }()
165 }
166 sig := c.resolveFuncType(d.Type, d.Recv)
167 fn := NewFunc(c.pkg, d.Name.Value, sig)
168 if d.Recv == nil {
169 // plain function — update the already-registered object
170 if obj, ok := c.pkg.scope.Lookup(d.Name.Value).(*Func); ok {
171 obj.typ = sig
172 }
173 return
174 }
175 // method — find the named receiver type and attach the method
176 recvTyp := c.resolveTypeExpr(d.Recv.Type)
177 if recvTyp == nil {
178 return
179 }
180 // unwrap pointer receiver: func (p *T) F() → receiver type is T
181 if pt, ok := recvTyp.(*Pointer); ok {
182 fn.hasPtrRecv = true
183 recvTyp = pt.base
184 }
185 if named, ok := recvTyp.(*Named); ok {
186 named.AddMethod(fn)
187 }
188 if c.info != nil && d.Name != nil {
189 c.info.Defs[d.Name] = fn
190 }
191 }
192
193 // checkFuncBody type-checks the body of a function declaration.
194 func (c *Checker) checkFuncBody(fd *syntax.FuncDecl) {
195 if fd.Body == nil {
196 return
197 }
198 scope := c.openScope(fd.Body, c.pkg.scope)
199 // Set localScope so resolveTypeExpr can find locally-defined types.
200 saved := c.localScope
201 c.localScope = scope
202 defer func() { c.localScope = saved }()
203 // Register all type parameters (function-level + receiver type args) in
204 // the body scope so the signature and body can reference them.
205 for _, name := range collectFuncTypeParams(fd) {
206 obj := NewTypeName(c.pkg, name, NewTypeParam(NewTypeName(c.pkg, name, nil), nil))
207 scope.Insert(obj)
208 }
209 sig := c.resolveFuncType(fd.Type, fd.Recv)
210 if sig != nil {
211 // Receiver variable (for methods).
212 if sig.recv != nil && sig.recv.name != "" {
213 scope.Insert(sig.recv)
214 }
215 // Parameters.
216 if sig.params != nil {
217 for i := 0; i < sig.params.Len(); i++ {
218 p := sig.params.At(i)
219 if p.name != "" {
220 scope.Insert(p)
221 }
222 }
223 }
224 // Named results.
225 if sig.results != nil {
226 for i := 0; i < sig.results.Len(); i++ {
227 r := sig.results.At(i)
228 if r.name != "" {
229 scope.Insert(r)
230 }
231 }
232 }
233 }
234 c.checkBlock(fd.Body, scope)
235 }
236
237 // collectFuncTypeParams returns all type parameter names visible in a function
238 // declaration: explicit type params on the function (TParamList) plus type
239 // arguments on a generic receiver (func (h Handle[T]) F() needs T in scope).
240 func collectFuncTypeParams(d *syntax.FuncDecl) []string {
241 seen := map[string]bool{}
242 var names []string
243 add := func(name string) {
244 if name != "" && !seen[name] {
245 seen[name] = true
246 names = append(names, name)
247 }
248 }
249 for _, tp := range d.TParamList {
250 if tp.Name != nil {
251 add(tp.Name.Value)
252 }
253 }
254 // Extract type args from a generic receiver: func (h Foo[T, U]) ...
255 if d.Recv != nil {
256 extractTypeArgs(d.Recv.Type, add)
257 }
258 return names
259 }
260
261 // extractTypeArgs recursively extracts bare Name nodes from a generic receiver
262 // type expression like Handle[T] or Ptr[K, V].
263 func extractTypeArgs(e syntax.Expr, add func(string)) {
264 if e == nil {
265 return
266 }
267 switch e := e.(type) {
268 case *syntax.IndexExpr:
269 // Handle[T] or Handle[T, U] (ListExpr inside)
270 extractTypeArgs(e.Index, add)
271 case *syntax.Name:
272 add(e.Value)
273 case *syntax.ListExpr:
274 for _, el := range e.ElemList {
275 extractTypeArgs(el, add)
276 }
277 case *syntax.Operation:
278 if e.Y == nil {
279 extractTypeArgs(e.X, add)
280 }
281 }
282 }
283
284 // declare inserts obj into s, recording the definition in info.
285 func (c *Checker) declare(s *Scope, id *syntax.Name, obj Object) {
286 if id != nil && id.Value != "_" {
287 if alt := s.Insert(obj); alt != nil {
288 c.errorf(id.Pos(), "%s redeclared in this block", id.Value)
289 return
290 }
291 }
292 if c.info != nil && id != nil {
293 c.info.Defs[id] = obj
294 }
295 }
296