tc_stmt.mx raw

   1  package types
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/syntax"
   5  	"git.smesh.lol/moxie/pkg/token"
   6  )
   7  
   8  // checkBlock type-checks a block statement. It always opens a fresh child
   9  // scope so that local declarations inside the block don't leak into the
  10  // parent scope and don't conflict with sibling declarations (e.g. var ok
  11  // in an if-body vs ok from the if-init).
  12  func (c *Checker) checkBlock(b *syntax.BlockStmt, parent *Scope) {
  13  	if b == nil {
  14  		return
  15  	}
  16  	scope := c.openScope(b, parent)
  17  	saved := c.localScope
  18  	c.localScope = scope
  19  	defer func() { c.localScope = saved }()
  20  	for _, s := range b.List {
  21  		c.checkStmt(s, scope)
  22  	}
  23  }
  24  
  25  func (c *Checker) checkStmt(s syntax.Stmt, scope *Scope) {
  26  	if s == nil {
  27  		return
  28  	}
  29  	switch ss := s.(type) {
  30  	case *syntax.EmptyStmt:
  31  		// nothing
  32  	case *syntax.ExprStmt:
  33  		c.checkExpr(ss.X, scope)
  34  	case *syntax.SendStmt:
  35  		c.checkExpr(ss.Chan, scope)
  36  		c.checkExpr(ss.Value, scope)
  37  	case *syntax.AssignStmt:
  38  		c.checkAssign(ss, scope)
  39  	case *syntax.BlockStmt:
  40  		inner := c.openScope(ss, scope)
  41  		c.checkBlock(ss, inner)
  42  	case *syntax.DeclStmt:
  43  		for _, d := range ss.DeclList {
  44  			c.checkLocalDecl(d, scope)
  45  		}
  46  	case *syntax.IfStmt:
  47  		inner := c.openScope(ss, scope)
  48  		if ss.Init != nil {
  49  			c.checkStmt(ss.Init, inner)
  50  		}
  51  		c.checkExpr(ss.Cond, inner)
  52  		c.checkBlock(ss.Then, inner)
  53  		if ss.Else != nil {
  54  			c.checkStmt(ss.Else, inner)
  55  		}
  56  	case *syntax.ForStmt:
  57  		inner := c.openScope(ss, scope)
  58  		if rc, ok := ss.Init.(*syntax.RangeClause); ok {
  59  			c.checkRange(rc, inner)
  60  		} else {
  61  			if ss.Init != nil {
  62  				c.checkStmt(ss.Init, inner)
  63  			}
  64  		}
  65  		if ss.Cond != nil {
  66  			c.checkExpr(ss.Cond, inner)
  67  		}
  68  		if ss.Post != nil {
  69  			c.checkStmt(ss.Post, inner)
  70  		}
  71  		c.checkBlock(ss.Body, inner)
  72  	case *syntax.SwitchStmt:
  73  		c.checkSwitch(ss, scope)
  74  	case *syntax.SelectStmt:
  75  		c.checkSelect(ss, scope)
  76  	case *syntax.ReturnStmt:
  77  		if ss.Results != nil {
  78  			c.checkExpr(ss.Results, scope)
  79  		}
  80  	case *syntax.BranchStmt:
  81  		// break/continue/goto/fallthrough - label checking deferred
  82  	case *syntax.LabeledStmt:
  83  		c.checkStmt(ss.Stmt, scope)
  84  	case *syntax.CallStmt:
  85  		// go/defer
  86  		c.checkExpr(ss.Call, scope)
  87  	}
  88  }
  89  
  90  func (c *Checker) checkAssign(s *syntax.AssignStmt, scope *Scope) {
  91  	if s.Rhs == nil {
  92  		// x++ or x--
  93  		c.checkExpr(s.Lhs, scope)
  94  		return
  95  	}
  96  	if s.Op == token.Def {
  97  		// short variable declaration: x, y := ...
  98  		c.checkShortVarDecl(s, scope)
  99  		return
 100  	}
 101  	c.checkExpr(s.Lhs, scope)
 102  	c.checkExpr(s.Rhs, scope)
 103  }
 104  
 105  func (c *Checker) checkShortVarDecl(s *syntax.AssignStmt, scope *Scope) {
 106  	// Evaluate rhs first.
 107  	rhsType := c.checkExpr(s.Rhs, scope)
 108  
 109  	lhsNames := exprNames(s.Lhs)
 110  	// Determine which names are new in the current scope (not parent scopes).
 111  	// Go spec: at least one name must be new; existing names are re-assigned.
 112  	newCount := 0
 113  	for _, name := range lhsNames {
 114  		if name.Value != "_" && scope.Lookup(name.Value) == nil {
 115  			newCount++
 116  		}
 117  	}
 118  	if newCount == 0 && len(lhsNames) > 0 {
 119  		// All names already declared in this scope: error.
 120  		c.errorf(s.Pos(), "no new variables on left side of :=")
 121  		return
 122  	}
 123  
 124  	// Assign types. Single-value: use rhsType directly.
 125  	// Multi-value: we don't yet unpack tuples, so leave type nil for existing names.
 126  	for i, name := range lhsNames {
 127  		if name.Value == "_" {
 128  			continue
 129  		}
 130  		if scope.Lookup(name.Value) != nil {
 131  			// Already in scope: re-assignment, not redeclaration. No action needed.
 132  			continue
 133  		}
 134  		var typ Type
 135  		if i == 0 && rhsType != nil {
 136  			typ = rhsType
 137  		}
 138  		obj := NewTCVar(c.pkg, name.Value, typ)
 139  		c.declare(scope, name, obj)
 140  	}
 141  }
 142  
 143  func (c *Checker) checkLocalDecl(d syntax.Decl, scope *Scope) {
 144  	switch dd := d.(type) {
 145  	case *syntax.VarDecl:
 146  		var typ Type
 147  		if dd.Type != nil {
 148  			typ = c.resolveTypeExpr(dd.Type)
 149  		}
 150  		if dd.Values != nil && typ == nil {
 151  			typ = c.checkExpr(dd.Values, scope)
 152  		}
 153  		for _, name := range dd.NameList {
 154  			obj := NewTCVar(c.pkg, name.Value, typ)
 155  			c.declare(scope, name, obj)
 156  		}
 157  	case *syntax.TypeDecl:
 158  		underlying := c.resolveTypeExpr(dd.Type)
 159  		obj := NewTypeName(c.pkg, dd.Name.Value, nil)
 160  		named := NewNamed(obj, underlying)
 161  		obj.Typ = named
 162  		c.declare(scope, dd.Name, obj)
 163  	case *syntax.ConstDecl:
 164  		var vals []ConstVal
 165  		if dd.Values != nil {
 166  			vals = c.evalConstExprList(dd.Values, 0)
 167  		}
 168  		var typ Type
 169  		if dd.Type != nil {
 170  			typ = c.resolveTypeExpr(dd.Type)
 171  		}
 172  		for i, name := range dd.NameList {
 173  			var cv ConstVal
 174  			if i < len(vals) {
 175  				cv = vals[i]
 176  			}
 177  			var ct Type
 178  			if typ != nil {
 179  				ct = typ
 180  			} else if cv != nil {
 181  				ct = UntypedTypeOfCV(cv)
 182  			}
 183  			obj := NewTCConst(c.pkg, name.Value, ct, cv)
 184  			c.declare(scope, name, obj)
 185  		}
 186  	}
 187  }
 188  
 189  func (c *Checker) checkRange(rc *syntax.RangeClause, scope *Scope) {
 190  	rangeType := c.checkExpr(rc.X, scope)
 191  	if rc.Lhs != nil && rc.Def {
 192  		names := exprNames(rc.Lhs)
 193  		keyType, valType := rangeKeyVal(rangeType)
 194  		types := []Type{keyType, valType}
 195  		for i, name := range names {
 196  			if name.Value == "_" {
 197  				continue
 198  			}
 199  			var t Type
 200  			if i < len(types) {
 201  				t = types[i]
 202  			}
 203  			obj := NewTCVar(c.pkg, name.Value, t)
 204  			c.declare(scope, name, obj)
 205  		}
 206  	}
 207  }
 208  
 209  func (c *Checker) checkSwitch(s *syntax.SwitchStmt, scope *Scope) {
 210  	inner := c.openScope(s, scope)
 211  	if s.Init != nil {
 212  		c.checkStmt(s.Init, inner)
 213  	}
 214  
 215  	// Type switch: switch v := x.(type) { case T: ... }
 216  	if s.Tag != nil {
 217  		if tsg, ok := s.Tag.(*syntax.TypeSwitchGuard); ok {
 218  			xTyp := c.checkExpr(tsg.X, inner)
 219  			for _, clause := range s.Body {
 220  				clauseScope := c.openScope(clause, inner)
 221  				// Bind the guard variable to the case type in each clause.
 222  				if tsg.Lhs != nil {
 223  					caseTyp := xTyp // default / multi-type: keep interface type
 224  					if clause.Cases != nil {
 225  						if _, multi := clause.Cases.(*syntax.ListExpr); !multi {
 226  							// nil case or single type: nil keeps interface type.
 227  							if n, isNil := clause.Cases.(*syntax.Name); !isNil || n.Value != "nil" {
 228  								if t := c.resolveTypeExpr(clause.Cases); t != nil {
 229  									caseTyp = t
 230  								}
 231  							}
 232  						}
 233  					}
 234  					obj := NewTCVar(c.pkg, tsg.Lhs.Value, caseTyp)
 235  					clauseScope.Insert(obj)
 236  					if c.info != nil {
 237  						c.info.Implicits[clause] = obj
 238  					}
 239  				}
 240  				for _, stmt := range clause.Body {
 241  					c.checkStmt(stmt, clauseScope)
 242  				}
 243  			}
 244  			return
 245  		}
 246  	}
 247  
 248  	// Expression switch.
 249  	if s.Tag != nil {
 250  		c.checkExpr(s.Tag, inner)
 251  	}
 252  	for _, clause := range s.Body {
 253  		clauseScope := c.openScope(clause, inner)
 254  		if clause.Cases != nil {
 255  			c.checkExpr(clause.Cases, clauseScope)
 256  		}
 257  		for _, stmt := range clause.Body {
 258  			c.checkStmt(stmt, clauseScope)
 259  		}
 260  	}
 261  }
 262  
 263  func (c *Checker) checkSelect(s *syntax.SelectStmt, scope *Scope) {
 264  	for _, clause := range s.Body {
 265  		clauseScope := c.openScope(clause, scope)
 266  		if clause.Comm != nil {
 267  			c.checkStmt(clause.Comm, clauseScope)
 268  		}
 269  		for _, stmt := range clause.Body {
 270  			c.checkStmt(stmt, clauseScope)
 271  		}
 272  	}
 273  }
 274  
 275  // exprNames extracts *syntax.Name nodes from an expression.
 276  // Used for the LHS of := and range clauses.
 277  func exprNames(e syntax.Expr) (ns []*syntax.Name) {
 278  	if e == nil {
 279  		return nil
 280  	}
 281  	if n, ok := e.(*syntax.Name); ok {
 282  		return []*syntax.Name{n}
 283  	}
 284  	if l, ok := e.(*syntax.ListExpr); ok {
 285  		var names []*syntax.Name
 286  		for _, el := range l.ElemList {
 287  			if n2, ok2 := el.(*syntax.Name); ok2 {
 288  				push(names, n2)
 289  			}
 290  		}
 291  		return names
 292  	}
 293  	return nil
 294  }
 295  
 296  // rangeKeyVal returns the key and value types for ranging over t.
 297  func rangeKeyVal(t Type) (key, val Type) {
 298  	if t == nil {
 299  		return nil, nil
 300  	}
 301  	switch ut := SafeUnderlying(t).(type) {
 302  	case *Array:
 303  		return Typ[Int32], ut.Elem
 304  	case *Slice:
 305  		return Typ[Int32], ut.Elem
 306  	case *TCMap:
 307  		return ut.Key, ut.Elem
 308  	case *TCChan:
 309  		return ut.Elem, nil
 310  	case *Basic:
 311  		if ut.Info&IsString != 0 {
 312  			return Typ[Int32], Typ[Uint8]
 313  		}
 314  	}
 315  	return nil, nil
 316  }
 317