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