tc_stmt.mx raw

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