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  	named := NewNamed(obj, nil)
 133  	underlying := c.resolveTypeExpr(d.Type)
 134  	named.SetUnderlying(underlying)
 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