stmt.go raw

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