tc_expr.mx raw

   1  package main
   2  
   3  // checkExpr type-checks an expression and returns its type.
   4  // It also records the type info in c.info if non-nil.
   5  func (c *Checker) checkExpr(e Expr, scope *Scope) Type {
   6  	if e == nil {
   7  		return nil
   8  	}
   9  	tv := c.typeExpr(e, scope)
  10  	c.record(e, tv)
  11  	return tv.Type
  12  }
  13  
  14  // typeExpr computes the TCTypeAndValue for expression e.
  15  func (c *Checker) typeExpr(e Expr, scope *Scope) TCTypeAndValue {
  16  	switch e := e.(type) {
  17  	case *Name:
  18  		return c.typeIdent(e, scope)
  19  	case *BasicLit:
  20  		return c.typeBasicLit(e)
  21  	case *Operation:
  22  		return c.typeOperation(e, scope)
  23  	case *CallExpr:
  24  		return c.typeCall(e, scope)
  25  	case *SelectorExpr:
  26  		return c.typeSelector(e, scope)
  27  	case *IndexExpr:
  28  		return c.typeIndex(e, scope)
  29  	case *SliceExpr:
  30  		return c.typeSlice(e, scope)
  31  	case *AssertExpr:
  32  		return c.typeAssert(e, scope)
  33  	case *TypeSwitchGuard:
  34  		return TCTypeAndValue{Type: c.checkExpr(e.X, scope), mode: modeValue}
  35  	case *CompositeLit:
  36  		return c.typeCompositeLit(e, scope)
  37  	case *FuncLit:
  38  		return c.typeFuncLit(e, scope)
  39  	case *KeyValueExpr:
  40  		// key:value - type is the value's type
  41  		c.checkExpr(e.Key, scope)
  42  		return c.typeExpr(e.Value, scope)
  43  	case *ParenExpr:
  44  		return c.typeExpr(e.X, scope)
  45  	case *ListExpr:
  46  		// multi-value list (tuple unpacking context)
  47  		var last TCTypeAndValue
  48  		for _, el := range e.ElemList {
  49  			last = c.typeExpr(el, scope)
  50  		}
  51  		return last
  52  	// type expressions used where a value is expected
  53  	case *SliceType, *ArrayType, *MapType,
  54  		*ChanType, *StructType, *InterfaceType,
  55  		*FuncType, *DotsType:
  56  		typ := c.resolveTypeExpr(e)
  57  		return TCTypeAndValue{Type: typ, mode: modeType}
  58  	}
  59  	return TCTypeAndValue{}
  60  }
  61  
  62  func (c *Checker) typeIdent(e *Name, scope *Scope) TCTypeAndValue {
  63  	if e.Value == "_" {
  64  		return TCTypeAndValue{mode: modeValue}
  65  	}
  66  	_, obj := c.lookup(e.Value, scope)
  67  	if obj == nil {
  68  		c.errorf(e.Pos(), "undefined: %s", e.Value)
  69  		return TCTypeAndValue{}
  70  	}
  71  	if c.info != nil {
  72  		c.info.Uses[e] = obj
  73  	}
  74  	switch obj := obj.(type) {
  75  	case *TCVar:
  76  		return TCTypeAndValue{Type: obj.typ, mode: modeVar}
  77  	case *TCConst:
  78  		return TCTypeAndValue{Type: obj.typ, Value: obj.val, mode: modeConst}
  79  	case *TypeName:
  80  		return TCTypeAndValue{Type: obj.typ, mode: modeType}
  81  	case *TCFunc:
  82  		return TCTypeAndValue{Type: obj.typ, mode: modeValue}
  83  	case *Builtin:
  84  		return TCTypeAndValue{mode: modeBuiltin}
  85  	case *PkgName:
  86  		return TCTypeAndValue{mode: modePkg}
  87  	}
  88  	return TCTypeAndValue{}
  89  }
  90  
  91  func (c *Checker) typeBasicLit(e *BasicLit) TCTypeAndValue {
  92  	switch e.Kind {
  93  	case IntLit:
  94  		return TCTypeAndValue{Type: Typ[UntypedInt], mode: modeConst}
  95  	case FloatLit:
  96  		return TCTypeAndValue{Type: Typ[UntypedFloat], mode: modeConst}
  97  	case StringLit:
  98  		return TCTypeAndValue{Type: Typ[UntypedString], mode: modeConst}
  99  	case RuneLit:
 100  		return TCTypeAndValue{Type: Typ[UntypedRune], mode: modeConst}
 101  	}
 102  	return TCTypeAndValue{}
 103  }
 104  
 105  func (c *Checker) typeOperation(e *Operation, scope *Scope) TCTypeAndValue {
 106  	if e.Y == nil {
 107  		// unary
 108  		xt := c.checkExpr(e.X, scope)
 109  		if xt == nil {
 110  			return TCTypeAndValue{mode: modeValue}
 111  		}
 112  		switch e.Op {
 113  		case Recv: // <-ch
 114  			if ch, ok := xt.Underlying().(*TCChan); ok {
 115  				return TCTypeAndValue{Type: ch.elem, mode: modeValue}
 116  			}
 117  		case And: // &x
 118  			return TCTypeAndValue{Type: NewPointer(xt), mode: modeValue}
 119  		case Mul: // *x (dereference)
 120  			if pt, ok := xt.Underlying().(*Pointer); ok {
 121  				return TCTypeAndValue{Type: pt.base, mode: modeVar}
 122  			}
 123  		default:
 124  			return TCTypeAndValue{Type: xt, mode: modeValue}
 125  		}
 126  		return TCTypeAndValue{mode: modeValue}
 127  	}
 128  	// binary
 129  	xt := c.checkExpr(e.X, scope)
 130  	yt := c.checkExpr(e.Y, scope)
 131  	switch e.Op {
 132  	case Eql, Neq, Lss, Leq, Gtr, Geq,
 133  		AndAnd, OrOr:
 134  		return TCTypeAndValue{Type: Typ[Bool], mode: modeValue}
 135  	case Or, Add:
 136  		// In Moxie, | on strings is concat. Use dominant side's type.
 137  		if xt != nil {
 138  			return TCTypeAndValue{Type: xt, mode: modeValue}
 139  		}
 140  		return TCTypeAndValue{Type: yt, mode: modeValue}
 141  	default:
 142  		if xt != nil {
 143  			return TCTypeAndValue{Type: xt, mode: modeValue}
 144  		}
 145  		return TCTypeAndValue{Type: yt, mode: modeValue}
 146  	}
 147  }
 148  
 149  func (c *Checker) typeCall(e *CallExpr, scope *Scope) TCTypeAndValue {
 150  	funTV := c.typeExpr(e.Fun, scope)
 151  	if c.info != nil {
 152  		c.info.Types[e.Fun] = funTV
 153  	}
 154  
 155  	for _, arg := range e.ArgList {
 156  		c.checkExpr(arg, scope)
 157  	}
 158  
 159  	if funTV.mode == modeBuiltin {
 160  		return c.typeBuiltinCall(e, scope)
 161  	}
 162  	if funTV.mode == modeType {
 163  		// conversion: T(x)
 164  		if len(e.ArgList) == 1 {
 165  			c.checkExpr(e.ArgList[0], scope)
 166  		}
 167  		return TCTypeAndValue{Type: funTV.Type, mode: modeValue}
 168  	}
 169  
 170  	if funTV.Type == nil {
 171  		return TCTypeAndValue{}
 172  	}
 173  	sig, ok := funTV.Type.Underlying().(*Signature)
 174  	if !ok {
 175  		return TCTypeAndValue{}
 176  	}
 177  	if sig.results == nil || sig.results.Len() == 0 {
 178  		return TCTypeAndValue{mode: modeVoid}
 179  	}
 180  	if sig.results.Len() == 1 {
 181  		return TCTypeAndValue{Type: sig.results.At(0).typ, mode: modeValue}
 182  	}
 183  	// multi-return: return a Tuple type
 184  	return TCTypeAndValue{Type: sig.results, mode: modeValue}
 185  }
 186  
 187  func (c *Checker) typeBuiltinCall(e *CallExpr, scope *Scope) TCTypeAndValue {
 188  	// Determine which builtin from the ident.
 189  	name, ok := e.Fun.(*Name)
 190  	if !ok {
 191  		return TCTypeAndValue{}
 192  	}
 193  	_, obj := c.lookup(name.Value, scope)
 194  	b, ok := obj.(*Builtin)
 195  	if !ok {
 196  		return TCTypeAndValue{}
 197  	}
 198  	switch b.id {
 199  	case BuiltinLen, BuiltinCap:
 200  		return TCTypeAndValue{Type: Typ[Int32], mode: modeValue}
 201  	case BuiltinAppend:
 202  		if len(e.ArgList) > 0 {
 203  			return TCTypeAndValue{Type: c.checkExpr(e.ArgList[0], scope), mode: modeValue}
 204  		}
 205  	case BuiltinMake:
 206  		if len(e.ArgList) > 0 {
 207  			return TCTypeAndValue{Type: c.resolveTypeExpr(e.ArgList[0]), mode: modeValue}
 208  		}
 209  	case BuiltinNew:
 210  		if len(e.ArgList) > 0 {
 211  			return TCTypeAndValue{Type: NewPointer(c.resolveTypeExpr(e.ArgList[0])), mode: modeValue}
 212  		}
 213  	case BuiltinPanic, BuiltinPrint, BuiltinPrintln:
 214  		return TCTypeAndValue{mode: modeVoid}
 215  	case BuiltinRecover:
 216  		return TCTypeAndValue{Type: universeError.typ, mode: modeValue}
 217  	case BuiltinClose, BuiltinDelete, BuiltinClear:
 218  		return TCTypeAndValue{mode: modeVoid}
 219  	case BuiltinCopy:
 220  		return TCTypeAndValue{Type: Typ[Int32], mode: modeValue}
 221  	case BuiltinSpawn:
 222  		return TCTypeAndValue{mode: modeVoid}
 223  	}
 224  	return TCTypeAndValue{}
 225  }
 226  
 227  func (c *Checker) typeSelector(e *SelectorExpr, scope *Scope) TCTypeAndValue {
 228  	xt := c.typeExpr(e.X, scope)
 229  	if c.info != nil {
 230  		c.info.Types[e.X] = xt
 231  	}
 232  	if xt.mode == modePkg {
 233  		// package.Name
 234  		if pkgName, ok := e.X.(*Name); ok {
 235  			_, pkgObj := c.lookup(pkgName.Value, scope)
 236  			if pn, ok := pkgObj.(*PkgName); ok && pn.imported != nil {
 237  				obj := pn.imported.scope.Lookup(e.Sel.Value)
 238  				if obj != nil {
 239  					if c.info != nil {
 240  						c.info.Uses[e.Sel] = obj
 241  					}
 242  					return TCTypeAndValue{Type: obj.Type(), mode: modeValue}
 243  				}
 244  			}
 245  		}
 246  		return TCTypeAndValue{}
 247  	}
 248  
 249  	// field or method lookup
 250  	typ := xt.Type
 251  	if typ == nil {
 252  		return TCTypeAndValue{}
 253  	}
 254  	sel := c.lookupFieldOrMethod(typ, e.Sel.Value)
 255  	if sel != nil {
 256  		if c.info != nil {
 257  			c.info.Selections[e] = sel
 258  			if sel.obj != nil {
 259  				c.info.Uses[e.Sel] = sel.obj
 260  			}
 261  		}
 262  		return TCTypeAndValue{Type: sel.Type(), mode: modeValue}
 263  	}
 264  	return TCTypeAndValue{}
 265  }
 266  
 267  func (c *Checker) typeIndex(e *IndexExpr, scope *Scope) TCTypeAndValue {
 268  	xt := c.checkExpr(e.X, scope)
 269  	c.checkExpr(e.Index, scope)
 270  	if xt == nil {
 271  		return TCTypeAndValue{}
 272  	}
 273  	switch t := xt.Underlying().(type) {
 274  	case *Array:
 275  		return TCTypeAndValue{Type: t.elem, mode: modeVar}
 276  	case *Slice:
 277  		return TCTypeAndValue{Type: t.elem, mode: modeVar}
 278  	case *TCMap:
 279  		return TCTypeAndValue{Type: t.elem, mode: modeValue}
 280  	}
 281  	return TCTypeAndValue{}
 282  }
 283  
 284  func (c *Checker) typeSlice(e *SliceExpr, scope *Scope) TCTypeAndValue {
 285  	xt := c.checkExpr(e.X, scope)
 286  	for _, idx := range e.Index {
 287  		if idx != nil {
 288  			c.checkExpr(idx, scope)
 289  		}
 290  	}
 291  	if xt == nil {
 292  		return TCTypeAndValue{}
 293  	}
 294  	switch t := xt.Underlying().(type) {
 295  	case *Array:
 296  		return TCTypeAndValue{Type: NewSlice(t.elem), mode: modeValue}
 297  	case *Slice:
 298  		return TCTypeAndValue{Type: xt, mode: modeValue}
 299  	}
 300  	// string[:] -> string
 301  	if b, ok := xt.Underlying().(*Basic); ok && b.info&IsString != 0 {
 302  		return TCTypeAndValue{Type: xt, mode: modeValue}
 303  	}
 304  	return TCTypeAndValue{}
 305  }
 306  
 307  func (c *Checker) typeAssert(e *AssertExpr, scope *Scope) TCTypeAndValue {
 308  	c.checkExpr(e.X, scope)
 309  	assertedType := c.resolveTypeExpr(e.Type)
 310  	return TCTypeAndValue{Type: assertedType, mode: modeValue}
 311  }
 312  
 313  func (c *Checker) typeCompositeLit(e *CompositeLit, scope *Scope) TCTypeAndValue {
 314  	var typ Type
 315  	if e.Type != nil {
 316  		typ = c.resolveTypeExpr(e.Type)
 317  	}
 318  	// Determine whether keys in key:value pairs are field names (struct) or
 319  	// expressions (map/slice). Struct field names are not in scope.
 320  	// Also treat typeless composite literals (e.Type == nil) as structs -
 321  	// in Go they appear as slice/array element literals where type is inferred.
 322  	isStruct := e.Type == nil || (typ != nil && isStructType(typ))
 323  	// Fallback: if we have key:value elements but couldn't determine the type,
 324  	// assume struct (struct field names don't exist as scope variables).
 325  	if !isStruct && len(e.ElemList) > 0 {
 326  		if _, ok := e.ElemList[0].(*KeyValueExpr); ok {
 327  			isStruct = true
 328  		}
 329  	}
 330  	for _, el := range e.ElemList {
 331  		if kv, ok := el.(*KeyValueExpr); ok {
 332  			if isStruct {
 333  				// Struct field: only check value, skip key lookup.
 334  				c.checkExpr(kv.Value, scope)
 335  			} else {
 336  				// Map/unknown: check both key and value.
 337  				c.checkExpr(kv.Key, scope)
 338  				c.checkExpr(kv.Value, scope)
 339  			}
 340  		} else {
 341  			c.checkExpr(el, scope)
 342  		}
 343  	}
 344  	return TCTypeAndValue{Type: typ, mode: modeValue}
 345  }
 346  
 347  // isStructType returns true if t is (or is a Named wrapping) a struct.
 348  // Named types with nil underlying (unresolved) are treated as structs
 349  // because composite literals with key:value syntax on a Named type are
 350  // always struct literals in Moxie code - map literals use an explicit
 351  // map[K]V type, never a Named alias at this level.
 352  func isStructType(t Type) bool {
 353  	if t == nil {
 354  		return false
 355  	}
 356  	switch t := t.(type) {
 357  	case *TCStruct:
 358  		return true
 359  	case *Named:
 360  		if t.underlying == nil {
 361  			return true // optimistic: Named with key:value syntax = struct
 362  		}
 363  		return isStructType(t.underlying)
 364  	}
 365  	return false
 366  }
 367  
 368  func (c *Checker) typeFuncLit(e *FuncLit, scope *Scope) TCTypeAndValue {
 369  	sig := c.resolveFuncType(e.Type, nil)
 370  	inner := c.openScope(e.Body, scope)
 371  	if sig != nil {
 372  		if sig.params != nil {
 373  			for i := 0; i < sig.params.Len(); i++ {
 374  				p := sig.params.At(i)
 375  				if p.name != "" {
 376  					inner.Insert(p)
 377  				}
 378  			}
 379  		}
 380  		if sig.results != nil {
 381  			for i := 0; i < sig.results.Len(); i++ {
 382  				r := sig.results.At(i)
 383  				if r.name != "" {
 384  					inner.Insert(r)
 385  				}
 386  			}
 387  		}
 388  	}
 389  	c.checkBlock(e.Body, inner)
 390  	return TCTypeAndValue{Type: sig, mode: modeValue}
 391  }
 392  
 393  // lookupFieldOrMethod finds a field or method named name on type t.
 394  func (c *Checker) lookupFieldOrMethod(t Type, name string) *Selection {
 395  	if t == nil {
 396  		return nil
 397  	}
 398  	// dereference pointer
 399  	indirect := false
 400  	if pt, ok := safeUnderlying(t).(*Pointer); ok {
 401  		t = pt.base
 402  		indirect = true
 403  	}
 404  
 405  	switch t := safeUnderlying(t).(type) {
 406  	case *TCStruct:
 407  		for i, f := range t.fields {
 408  			if f.name == name {
 409  				return &Selection{
 410  					kind:  FieldVal,
 411  					recv:  t,
 412  					obj:   f,
 413  					index: []int{i},
 414  					indir: indirect,
 415  				}
 416  			}
 417  		}
 418  	case *TCInterface:
 419  		for _, m := range t.allMethods {
 420  			if m.name == name {
 421  				fn := NewTCFunc(nil, name, m.sig)
 422  				return &Selection{kind: MethodVal, recv: t, obj: fn, indir: indirect}
 423  			}
 424  		}
 425  	case *Named:
 426  		for _, m := range t.methods {
 427  			if m.name == name {
 428  				return &Selection{kind: MethodVal, recv: t, obj: m, indir: indirect}
 429  			}
 430  		}
 431  		if t.underlying != nil {
 432  			return c.lookupFieldOrMethod(t.underlying, name)
 433  		}
 434  	}
 435  	return nil
 436  }
 437