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