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