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