tc_assign.mx raw

   1  package types
   2  
   3  // Identical reports whether T and U are identical types under Moxie rules:
   4  // - string = []byte (Moxie text unification)
   5  // - int32 = int32, uint32 = uint32 (enforced via universe scope, but checked here too)
   6  func Identical(T, U Type) (ok bool) {
   7  	if T == U {
   8  		return true
   9  	}
  10  	if T == nil || U == nil {
  11  		return false
  12  	}
  13  	switch t := T.(type) {
  14  	case *Basic:
  15  		var u *Basic
  16  		u, ok = U.(*Basic)
  17  		if !ok {
  18  			if t.Info&IsString != 0 {
  19  				sl, slOk := U.(*Slice)
  20  				if slOk {
  21  					eb, ebOk := sl.Elem.(*Basic)
  22  					if ebOk && eb.Kind == Uint8 {
  23  						return true
  24  					}
  25  				}
  26  			}
  27  			return false
  28  		}
  29  		tk := normaliseKind(t.Kind)
  30  		uk := normaliseKind(u.Kind)
  31  		if tk == uk {
  32  			return true
  33  		}
  34  		if t.Info&IsString != 0 && u.Info&IsString != 0 {
  35  			return true
  36  		}
  37  		return false
  38  	case *Pointer:
  39  		var u *Pointer
  40  		u, ok = U.(*Pointer)
  41  		return ok && Identical(t.Base, u.Base)
  42  	case *Array:
  43  		var u *Array
  44  		u, ok = U.(*Array)
  45  		return ok && t.Len == u.Len && Identical(t.Elem, u.Elem)
  46  	case *Slice:
  47  		var u *Slice
  48  		u, ok = U.(*Slice)
  49  		if !ok {
  50  			// []byte = string in Moxie (including untyped string)
  51  			b, bOk := U.(*Basic)
  52  			if bOk && b.Info&IsString != 0 {
  53  				eb, ebOk := t.Elem.(*Basic)
  54  				if ebOk && eb.Kind == Uint8 {
  55  					return true
  56  				}
  57  			}
  58  			return false
  59  		}
  60  		return Identical(t.Elem, u.Elem)
  61  	case *TCMap:
  62  		var u *TCMap
  63  		u, ok = U.(*TCMap)
  64  		return ok && Identical(t.Key, u.Key) && Identical(t.Elem, u.Elem)
  65  	case *TCChan:
  66  		var u *TCChan
  67  		u, ok = U.(*TCChan)
  68  		return ok && t.Dir == u.Dir && Identical(t.Elem, u.Elem)
  69  	case *TCStruct:
  70  		var u *TCStruct
  71  		u, ok = U.(*TCStruct)
  72  		if !ok || t.NumFields() != u.NumFields() {
  73  			return false
  74  		}
  75  		for i := range t.Fields {
  76  			tf := t.Fields[i]
  77  			uf := u.Fields[i]
  78  			if tf.Name != uf.Name || tf.Anonymous != uf.Anonymous {
  79  				return false
  80  			}
  81  			if !Identical(tf.Typ, uf.Typ) {
  82  				return false
  83  			}
  84  		}
  85  		return true
  86  	case *TCInterface:
  87  		var u *TCInterface
  88  		u, ok = U.(*TCInterface)
  89  		if !ok {
  90  			return false
  91  		}
  92  		if t.NumMethods() != u.NumMethods() {
  93  			return false
  94  		}
  95  		for i, tm := range t.AllMethods {
  96  			um := u.AllMethods[i]
  97  			if tm.Name != um.Name || !Identical(tm.Sig, um.Sig) {
  98  				return false
  99  			}
 100  		}
 101  		return true
 102  	case *Signature:
 103  		var u *Signature
 104  		u, ok = U.(*Signature)
 105  		if !ok {
 106  			return false
 107  		}
 108  		if t.Variadic != u.Variadic {
 109  			return false
 110  		}
 111  		if !identicalTuples(t.Params, u.Params) || !identicalTuples(t.Results, u.Results) {
 112  			return false
 113  		}
 114  		return true
 115  	case *Named:
 116  		var u *Named
 117  		u, ok = U.(*Named)
 118  		return ok && t.Obj == u.Obj
 119  	case *TypeParam:
 120  		var u *TypeParam
 121  		u, ok = U.(*TypeParam)
 122  		return ok && t.Id == u.Id
 123  	case *Tuple:
 124  		var u *Tuple
 125  		u, ok = U.(*Tuple)
 126  		return ok && identicalTuples(t, u)
 127  	}
 128  	return false
 129  }
 130  
 131  func identicalTuples(a, b *Tuple) (ok bool) {
 132  	la, lb := tupleLen(a), tupleLen(b)
 133  	if la != lb {
 134  		return false
 135  	}
 136  	for i := 0; i < la; i++ {
 137  		if !Identical(a.Vars[i].Typ, b.Vars[i].Typ) {
 138  			return false
 139  		}
 140  	}
 141  	return true
 142  }
 143  
 144  func tupleLen(t *Tuple) (n int32) {
 145  	if t == nil {
 146  		return 0
 147  	}
 148  	return t.Len()
 149  }
 150  
 151  // normaliseKind maps int32/uint32 to their 32-bit equivalents for identity checks.
 152  func normaliseKind(k BasicKind) (b BasicKind) {
 153  	return k // int32=int32 is handled by the universe scope; no extra mapping needed
 154  }
 155  
 156  // Assignable reports whether a value of type V is assignable to type T
 157  // under Moxie's type rules (spec Assignability, plus Moxie extensions).
 158  func Assignable(V, T Type) (ok bool) {
 159  	if V == nil || T == nil {
 160  		return V == T
 161  	}
 162  	// Identical types are always assignable.
 163  	if Identical(V, T) {
 164  		return true
 165  	}
 166  	// Moxie extension: string = []byte anywhere.
 167  	if isTextType(V) && isTextType(T) {
 168  		return true
 169  	}
 170  	// Untyped constants are assignable to any numeric/bool/string type they fit in.
 171  	bv, bvOk := V.(*Basic)
 172  	if bvOk && bv.Info&IsUntyped != 0 {
 173  		return untypedAssignable(bv, T)
 174  	}
 175  	// nil is assignable to pointer, slice, map, chan, func, interface.
 176  	if V == Typ[UntypedNil] {
 177  		return isNilable(T)
 178  	}
 179  	// V's underlying type identical to T's underlying type, and at least one is unnamed.
 180  	vu, tu := SafeUnderlying(V), SafeUnderlying(T)
 181  	if Identical(vu, tu) {
 182  		_, vNamed := V.(*Named)
 183  		_, tNamed := T.(*Named)
 184  		if !vNamed || !tNamed {
 185  			return true
 186  		}
 187  	}
 188  	// T is an interface and V implements it.
 189  	iface, ifOk := SafeUnderlying(T).(*TCInterface)
 190  	if ifOk {
 191  		return Implements(V, iface)
 192  	}
 193  	// Directional channel: bidirectional chan is assignable to directed chan.
 194  	tc, tcOk := T.(*TCChan)
 195  	if tcOk {
 196  		vc, vcOk := V.(*TCChan)
 197  		if vcOk {
 198  			if Identical(tc.Elem, vc.Elem) {
 199  				return vc.Dir == TCSendRecv || vc.Dir == tc.Dir
 200  			}
 201  		}
 202  	}
 203  	return false
 204  }
 205  
 206  func isNilable(T Type) (ok bool) {
 207  	switch SafeUnderlying(T).(type) {
 208  	case *Pointer, *Slice, *TCMap, *TCChan, *Signature, *TCInterface:
 209  		return true
 210  	}
 211  	return false
 212  }
 213  
 214  func untypedAssignable(V *Basic, T Type) (ok bool) {
 215  	var b *Basic
 216  	b, ok = SafeUnderlying(T).(*Basic)
 217  	if !ok {
 218  		return false
 219  	}
 220  	switch V.Kind {
 221  	case UntypedBool:
 222  		return b.Info&IsBoolean != 0
 223  	case UntypedInt, UntypedRune:
 224  		return b.Info&IsInteger != 0 || b.Info&IsFloat != 0
 225  	case UntypedFloat:
 226  		return b.Info&IsFloat != 0
 227  	case UntypedString:
 228  		return b.Info&IsString != 0
 229  	}
 230  	return false
 231  }
 232  
 233  // isTextType returns true if T is string or []byte under Moxie's unified model.
 234  func isTextType(T Type) (ok bool) {
 235  	b, bOk := SafeUnderlying(T).(*Basic)
 236  	if bOk && b.Info&IsString != 0 {
 237  		return true
 238  	}
 239  	sl, slOk := SafeUnderlying(T).(*Slice)
 240  	if slOk {
 241  		eb, ebOk := SafeUnderlying(sl.Elem).(*Basic)
 242  		if ebOk && eb.Kind == Uint8 {
 243  			return true
 244  		}
 245  	}
 246  	return false
 247  }
 248  
 249  // Implements reports whether type T implements interface I.
 250  func Implements(T Type, I *TCInterface) (ok bool) {
 251  	if I.IsEmpty() {
 252  		return true
 253  	}
 254  	// Collect T's method set.
 255  	ms := methodSet(T)
 256  	for _, im := range I.AllMethods {
 257  		var m *TCFunc
 258  		m, ok = ms[im.Name]
 259  		if !ok {
 260  			return false
 261  		}
 262  		if !Identical(m.Signature(), im.Sig) {
 263  			return false
 264  		}
 265  	}
 266  	return true
 267  }
 268  
 269  // methodSet returns the named method set of T as a name->Func map.
 270  // Includes methods from *T if T is a named type (pointer receiver methods).
 271  func methodSet(T Type) (m map[string]*TCFunc) {
 272  	ms := map[string]*TCFunc{}
 273  	collectMethods(T, ms, false)
 274  	return ms
 275  }
 276  
 277  func collectMethods(T Type, ms map[string]*TCFunc, ptrOk bool) {
 278  	switch t := T.(type) {
 279  	case *Named:
 280  		for _, m := range t.Methods {
 281  			if _, dup := ms[m.Name]; !dup {
 282  				ms[m.Name] = m
 283  			}
 284  		}
 285  		// Also collect pointer receiver methods if T is addressable.
 286  		if ptrOk {
 287  			collectMethods(NewPointer(T), ms, false)
 288  		}
 289  		collectMethods(t.Under, ms, ptrOk)
 290  	case *Pointer:
 291  		if named, ok := t.Base.(*Named); ok {
 292  			for _, m := range named.Methods {
 293  				if _, dup := ms[m.Name]; !dup {
 294  					ms[m.Name] = m
 295  				}
 296  			}
 297  		}
 298  	case *TCInterface:
 299  		for _, m := range t.AllMethods {
 300  			if _, dup := ms[m.Name]; !dup {
 301  				ms[m.Name] = NewTCFunc(nil, m.Name, m.Sig)
 302  			}
 303  		}
 304  	}
 305  }
 306