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  			scope.Insert(sig.recv)
 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 != "" {
 224  					scope.Insert(p)
 225  				}
 226  			}
 227  		}
 228  		// Named results.
 229  		if sig.results != nil {
 230  			for i := 0; i < sig.results.Len(); i++ {
 231  				r := sig.results.At(i)
 232  				if r.name != "" {
 233  					scope.Insert(r)
 234  				}
 235  			}
 236  		}
 237  	}
 238  	c.checkBlock(fd.Body, scope)
 239  }
 240  
 241  // collectFuncTypeParams returns all type parameter names visible in a function
 242  // declaration: explicit type params on the function (TParamList) plus type
 243  // arguments on a generic receiver (func (h Handle[T]) F() needs T in scope).
 244  func collectFuncTypeParams(d *FuncDecl) []string {
 245  	seen := map[string]bool{}
 246  	var names []string
 247  	add := func(name string) {
 248  		if name != "" && !seen[name] {
 249  			seen[name] = true
 250  			names = append(names, name)
 251  		}
 252  	}
 253  	for _, tp := range d.TParamList {
 254  		if tp.Name != nil {
 255  			add(tp.Name.Value)
 256  		}
 257  	}
 258  	// Extract type args from a generic receiver: func (h Foo[T, U]) ...
 259  	if d.Recv != nil {
 260  		extractTypeArgs(d.Recv.Type, add)
 261  	}
 262  	return names
 263  }
 264  
 265  // extractTypeArgs recursively extracts bare Name nodes from a generic receiver
 266  // type expression like Handle[T] or Ptr[K, V].
 267  func extractTypeArgs(e Expr, add func(string)) {
 268  	if e == nil {
 269  		return
 270  	}
 271  	switch e := e.(type) {
 272  	case *IndexExpr:
 273  		// Handle[T] or Handle[T, U] (ListExpr inside)
 274  		extractTypeArgs(e.Index, add)
 275  	case *Name:
 276  		add(e.Value)
 277  	case *ListExpr:
 278  		for _, el := range e.ElemList {
 279  			extractTypeArgs(el, add)
 280  		}
 281  	case *Operation:
 282  		if e.Y == nil {
 283  			extractTypeArgs(e.X, add)
 284  		}
 285  	}
 286  }
 287  
 288  // declare inserts obj into s, recording the definition in info.
 289  func (c *Checker) declare(s *Scope, id *Name, obj Object) {
 290  	if id != nil && id.Value != "_" {
 291  		if alt := s.Insert(obj); alt != nil {
 292  			c.errorf(id.Pos(), "%s redeclared in this block", id.Value)
 293  			return
 294  		}
 295  	}
 296  	if c.info != nil && id != nil {
 297  		c.info.Defs[id] = obj
 298  	}
 299  }
 300