tc_assign.mx raw

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