...

Source file src/go/types/subst.go

Documentation: go/types

		 1  // Copyright 2018 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  // This file implements instantiation of generic types
		 6  // through substitution of type parameters by actual
		 7  // types.
		 8  
		 9  package types
		10  
		11  import (
		12  	"bytes"
		13  	"fmt"
		14  	"go/token"
		15  )
		16  
		17  // TODO(rFindley) decide error codes for the errors in this file, and check
		18  //								if error spans can be improved
		19  
		20  type substMap struct {
		21  	// The targs field is currently needed for *Named type substitution.
		22  	// TODO(gri) rewrite that code, get rid of this field, and make this
		23  	//					 struct just the map (proj)
		24  	targs []Type
		25  	proj	map[*_TypeParam]Type
		26  }
		27  
		28  // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
		29  // If targs[i] is nil, tpars[i] is not substituted.
		30  func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
		31  	assert(len(tpars) == len(targs))
		32  	proj := make(map[*_TypeParam]Type, len(tpars))
		33  	for i, tpar := range tpars {
		34  		// We must expand type arguments otherwise *instance
		35  		// types end up as components in composite types.
		36  		// TODO(gri) explain why this causes problems, if it does
		37  		targ := expand(targs[i]) // possibly nil
		38  		targs[i] = targ
		39  		proj[tpar.typ.(*_TypeParam)] = targ
		40  	}
		41  	return &substMap{targs, proj}
		42  }
		43  
		44  func (m *substMap) String() string {
		45  	return fmt.Sprintf("%s", m.proj)
		46  }
		47  
		48  func (m *substMap) empty() bool {
		49  	return len(m.proj) == 0
		50  }
		51  
		52  func (m *substMap) lookup(tpar *_TypeParam) Type {
		53  	if t := m.proj[tpar]; t != nil {
		54  		return t
		55  	}
		56  	return tpar
		57  }
		58  
		59  func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) {
		60  	if trace {
		61  		check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
		62  		check.indent++
		63  		defer func() {
		64  			check.indent--
		65  			var under Type
		66  			if res != nil {
		67  				// Calling under() here may lead to endless instantiations.
		68  				// Test case: type T[P any] T[P]
		69  				// TODO(gri) investigate if that's a bug or to be expected.
		70  				under = res.Underlying()
		71  			}
		72  			check.trace(pos, "=> %s (under = %s)", res, under)
		73  		}()
		74  	}
		75  
		76  	assert(len(poslist) <= len(targs))
		77  
		78  	// TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
		79  	var tparams []*TypeName
		80  	switch t := typ.(type) {
		81  	case *Named:
		82  		tparams = t.tparams
		83  	case *Signature:
		84  		tparams = t.tparams
		85  		defer func() {
		86  			// If we had an unexpected failure somewhere don't panic below when
		87  			// asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
		88  			// is returned.
		89  			if _, ok := res.(*Signature); !ok {
		90  				return
		91  			}
		92  			// If the signature doesn't use its type parameters, subst
		93  			// will not make a copy. In that case, make a copy now (so
		94  			// we can set tparams to nil w/o causing side-effects).
		95  			if t == res {
		96  				copy := *t
		97  				res = &copy
		98  			}
		99  			// After instantiating a generic signature, it is not generic
	 100  			// anymore; we need to set tparams to nil.
	 101  			res.(*Signature).tparams = nil
	 102  		}()
	 103  
	 104  	default:
	 105  		check.dump("%v: cannot instantiate %v", pos, typ)
	 106  		unreachable() // only defined types and (defined) functions can be generic
	 107  	}
	 108  
	 109  	// the number of supplied types must match the number of type parameters
	 110  	if len(targs) != len(tparams) {
	 111  		// TODO(gri) provide better error message
	 112  		check.errorf(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), len(tparams))
	 113  		return Typ[Invalid]
	 114  	}
	 115  
	 116  	if len(tparams) == 0 {
	 117  		return typ // nothing to do (minor optimization)
	 118  	}
	 119  
	 120  	smap := makeSubstMap(tparams, targs)
	 121  
	 122  	// check bounds
	 123  	for i, tname := range tparams {
	 124  		tpar := tname.typ.(*_TypeParam)
	 125  		iface := tpar.Bound()
	 126  		if iface.Empty() {
	 127  			continue // no type bound
	 128  		}
	 129  
	 130  		targ := targs[i]
	 131  
	 132  		// best position for error reporting
	 133  		pos := pos
	 134  		if i < len(poslist) {
	 135  			pos = poslist[i]
	 136  		}
	 137  
	 138  		// The type parameter bound is parameterized with the same type parameters
	 139  		// as the instantiated type; before we can use it for bounds checking we
	 140  		// need to instantiate it with the type arguments with which we instantiate
	 141  		// the parameterized type.
	 142  		iface = check.subst(pos, iface, smap).(*Interface)
	 143  
	 144  		// targ must implement iface (methods)
	 145  		// - check only if we have methods
	 146  		check.completeInterface(token.NoPos, iface)
	 147  		if len(iface.allMethods) > 0 {
	 148  			// If the type argument is a pointer to a type parameter, the type argument's
	 149  			// method set is empty.
	 150  			// TODO(gri) is this what we want? (spec question)
	 151  			if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
	 152  				check.errorf(atPos(pos), 0, "%s has no methods", targ)
	 153  				break
	 154  			}
	 155  			if m, wrong := check.missingMethod(targ, iface, true); m != nil {
	 156  				// TODO(gri) needs to print updated name to avoid major confusion in error message!
	 157  				//					 (print warning for now)
	 158  				// Old warning:
	 159  				// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
	 160  				if m.name == "==" {
	 161  					// We don't want to report "missing method ==".
	 162  					check.softErrorf(atPos(pos), 0, "%s does not satisfy comparable", targ)
	 163  				} else if wrong != nil {
	 164  					// TODO(gri) This can still report uninstantiated types which makes the error message
	 165  					//					 more difficult to read then necessary.
	 166  					// TODO(rFindley) should this use parentheses rather than ':' for qualification?
	 167  					check.softErrorf(atPos(pos), _Todo,
	 168  						"%s does not satisfy %s: wrong method signature\n\tgot	%s\n\twant %s",
	 169  						targ, tpar.bound, wrong, m,
	 170  					)
	 171  				} else {
	 172  					check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
	 173  				}
	 174  				break
	 175  			}
	 176  		}
	 177  
	 178  		// targ's underlying type must also be one of the interface types listed, if any
	 179  		if iface.allTypes == nil {
	 180  			continue // nothing to do
	 181  		}
	 182  
	 183  		// If targ is itself a type parameter, each of its possible types, but at least one, must be in the
	 184  		// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
	 185  		if targ := asTypeParam(targ); targ != nil {
	 186  			targBound := targ.Bound()
	 187  			if targBound.allTypes == nil {
	 188  				check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
	 189  				break
	 190  			}
	 191  			for _, t := range unpackType(targBound.allTypes) {
	 192  				if !iface.isSatisfiedBy(t) {
	 193  					// TODO(gri) match this error message with the one below (or vice versa)
	 194  					check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
	 195  					break
	 196  				}
	 197  			}
	 198  			break
	 199  		}
	 200  
	 201  		// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
	 202  		if !iface.isSatisfiedBy(targ) {
	 203  			check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes)
	 204  			break
	 205  		}
	 206  	}
	 207  
	 208  	return check.subst(pos, typ, smap)
	 209  }
	 210  
	 211  // subst returns the type typ with its type parameters tpars replaced by
	 212  // the corresponding type arguments targs, recursively.
	 213  // subst is functional in the sense that it doesn't modify the incoming
	 214  // type. If a substitution took place, the result type is different from
	 215  // from the incoming type.
	 216  func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
	 217  	if smap.empty() {
	 218  		return typ
	 219  	}
	 220  
	 221  	// common cases
	 222  	switch t := typ.(type) {
	 223  	case *Basic:
	 224  		return typ // nothing to do
	 225  	case *_TypeParam:
	 226  		return smap.lookup(t)
	 227  	}
	 228  
	 229  	// general case
	 230  	subst := subster{check, pos, make(map[Type]Type), smap}
	 231  	return subst.typ(typ)
	 232  }
	 233  
	 234  type subster struct {
	 235  	check *Checker
	 236  	pos	 token.Pos
	 237  	cache map[Type]Type
	 238  	smap	*substMap
	 239  }
	 240  
	 241  func (subst *subster) typ(typ Type) Type {
	 242  	switch t := typ.(type) {
	 243  	case nil:
	 244  		// Call typOrNil if it's possible that typ is nil.
	 245  		panic("nil typ")
	 246  
	 247  	case *Basic, *bottom, *top:
	 248  		// nothing to do
	 249  
	 250  	case *Array:
	 251  		elem := subst.typOrNil(t.elem)
	 252  		if elem != t.elem {
	 253  			return &Array{len: t.len, elem: elem}
	 254  		}
	 255  
	 256  	case *Slice:
	 257  		elem := subst.typOrNil(t.elem)
	 258  		if elem != t.elem {
	 259  			return &Slice{elem: elem}
	 260  		}
	 261  
	 262  	case *Struct:
	 263  		if fields, copied := subst.varList(t.fields); copied {
	 264  			return &Struct{fields: fields, tags: t.tags}
	 265  		}
	 266  
	 267  	case *Pointer:
	 268  		base := subst.typ(t.base)
	 269  		if base != t.base {
	 270  			return &Pointer{base: base}
	 271  		}
	 272  
	 273  	case *Tuple:
	 274  		return subst.tuple(t)
	 275  
	 276  	case *Signature:
	 277  		// TODO(gri) rethink the recv situation with respect to methods on parameterized types
	 278  		// recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain
	 279  		recv := t.recv
	 280  		params := subst.tuple(t.params)
	 281  		results := subst.tuple(t.results)
	 282  		if recv != t.recv || params != t.params || results != t.results {
	 283  			return &Signature{
	 284  				rparams: t.rparams,
	 285  				// TODO(rFindley) why can't we nil out tparams here, rather than in
	 286  				//								instantiate above?
	 287  				tparams:	t.tparams,
	 288  				scope:		t.scope,
	 289  				recv:		 recv,
	 290  				params:	 params,
	 291  				results:	results,
	 292  				variadic: t.variadic,
	 293  			}
	 294  		}
	 295  
	 296  	case *_Sum:
	 297  		types, copied := subst.typeList(t.types)
	 298  		if copied {
	 299  			// Don't do it manually, with a Sum literal: the new
	 300  			// types list may not be unique and NewSum may remove
	 301  			// duplicates.
	 302  			return _NewSum(types)
	 303  		}
	 304  
	 305  	case *Interface:
	 306  		methods, mcopied := subst.funcList(t.methods)
	 307  		types := t.types
	 308  		if t.types != nil {
	 309  			types = subst.typ(t.types)
	 310  		}
	 311  		embeddeds, ecopied := subst.typeList(t.embeddeds)
	 312  		if mcopied || types != t.types || ecopied {
	 313  			iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
	 314  			subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
	 315  			subst.check.completeInterface(token.NoPos, iface)
	 316  			return iface
	 317  		}
	 318  
	 319  	case *Map:
	 320  		key := subst.typ(t.key)
	 321  		elem := subst.typ(t.elem)
	 322  		if key != t.key || elem != t.elem {
	 323  			return &Map{key: key, elem: elem}
	 324  		}
	 325  
	 326  	case *Chan:
	 327  		elem := subst.typ(t.elem)
	 328  		if elem != t.elem {
	 329  			return &Chan{dir: t.dir, elem: elem}
	 330  		}
	 331  
	 332  	case *Named:
	 333  		subst.check.indent++
	 334  		defer func() {
	 335  			subst.check.indent--
	 336  		}()
	 337  		dump := func(format string, args ...interface{}) {
	 338  			if trace {
	 339  				subst.check.trace(subst.pos, format, args...)
	 340  			}
	 341  		}
	 342  
	 343  		if t.tparams == nil {
	 344  			dump(">>> %s is not parameterized", t)
	 345  			return t // type is not parameterized
	 346  		}
	 347  
	 348  		var newTargs []Type
	 349  
	 350  		if len(t.targs) > 0 {
	 351  			// already instantiated
	 352  			dump(">>> %s already instantiated", t)
	 353  			assert(len(t.targs) == len(t.tparams))
	 354  			// For each (existing) type argument targ, determine if it needs
	 355  			// to be substituted; i.e., if it is or contains a type parameter
	 356  			// that has a type argument for it.
	 357  			for i, targ := range t.targs {
	 358  				dump(">>> %d targ = %s", i, targ)
	 359  				newTarg := subst.typ(targ)
	 360  				if newTarg != targ {
	 361  					dump(">>> substituted %d targ %s => %s", i, targ, newTarg)
	 362  					if newTargs == nil {
	 363  						newTargs = make([]Type, len(t.tparams))
	 364  						copy(newTargs, t.targs)
	 365  					}
	 366  					newTargs[i] = newTarg
	 367  				}
	 368  			}
	 369  
	 370  			if newTargs == nil {
	 371  				dump(">>> nothing to substitute in %s", t)
	 372  				return t // nothing to substitute
	 373  			}
	 374  		} else {
	 375  			// not yet instantiated
	 376  			dump(">>> first instantiation of %s", t)
	 377  			// TODO(rFindley) can we instead subst the tparam types here?
	 378  			newTargs = subst.smap.targs
	 379  		}
	 380  
	 381  		// before creating a new named type, check if we have this one already
	 382  		h := instantiatedHash(t, newTargs)
	 383  		dump(">>> new type hash: %s", h)
	 384  		if named, found := subst.check.typMap[h]; found {
	 385  			dump(">>> found %s", named)
	 386  			subst.cache[t] = named
	 387  			return named
	 388  		}
	 389  
	 390  		// create a new named type and populate caches to avoid endless recursion
	 391  		tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
	 392  		named := subst.check.newNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
	 393  		named.tparams = t.tparams																		 // new type is still parameterized
	 394  		named.targs = newTargs
	 395  		subst.check.typMap[h] = named
	 396  		subst.cache[t] = named
	 397  
	 398  		// do the substitution
	 399  		dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
	 400  		named.underlying = subst.typOrNil(t.underlying)
	 401  		named.orig = named.underlying // for cycle detection (Checker.validType)
	 402  
	 403  		return named
	 404  
	 405  	case *_TypeParam:
	 406  		return subst.smap.lookup(t)
	 407  
	 408  	case *instance:
	 409  		// TODO(gri) can we avoid the expansion here and just substitute the type parameters?
	 410  		return subst.typ(t.expand())
	 411  
	 412  	default:
	 413  		panic("unimplemented")
	 414  	}
	 415  
	 416  	return typ
	 417  }
	 418  
	 419  // TODO(gri) Eventually, this should be more sophisticated.
	 420  //					 It won't work correctly for locally declared types.
	 421  func instantiatedHash(typ *Named, targs []Type) string {
	 422  	var buf bytes.Buffer
	 423  	writeTypeName(&buf, typ.obj, nil)
	 424  	buf.WriteByte('[')
	 425  	writeTypeList(&buf, targs, nil, nil)
	 426  	buf.WriteByte(']')
	 427  
	 428  	// With respect to the represented type, whether a
	 429  	// type is fully expanded or stored as instance
	 430  	// does not matter - they are the same types.
	 431  	// Remove the instanceMarkers printed for instances.
	 432  	res := buf.Bytes()
	 433  	i := 0
	 434  	for _, b := range res {
	 435  		if b != instanceMarker {
	 436  			res[i] = b
	 437  			i++
	 438  		}
	 439  	}
	 440  
	 441  	return string(res[:i])
	 442  }
	 443  
	 444  func typeListString(list []Type) string {
	 445  	var buf bytes.Buffer
	 446  	writeTypeList(&buf, list, nil, nil)
	 447  	return buf.String()
	 448  }
	 449  
	 450  // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
	 451  // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
	 452  // where an array/slice element is accessed before it is set up.
	 453  func (subst *subster) typOrNil(typ Type) Type {
	 454  	if typ == nil {
	 455  		return Typ[Invalid]
	 456  	}
	 457  	return subst.typ(typ)
	 458  }
	 459  
	 460  func (subst *subster) var_(v *Var) *Var {
	 461  	if v != nil {
	 462  		if typ := subst.typ(v.typ); typ != v.typ {
	 463  			copy := *v
	 464  			copy.typ = typ
	 465  			return &copy
	 466  		}
	 467  	}
	 468  	return v
	 469  }
	 470  
	 471  func (subst *subster) tuple(t *Tuple) *Tuple {
	 472  	if t != nil {
	 473  		if vars, copied := subst.varList(t.vars); copied {
	 474  			return &Tuple{vars: vars}
	 475  		}
	 476  	}
	 477  	return t
	 478  }
	 479  
	 480  func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
	 481  	out = in
	 482  	for i, v := range in {
	 483  		if w := subst.var_(v); w != v {
	 484  			if !copied {
	 485  				// first variable that got substituted => allocate new out slice
	 486  				// and copy all variables
	 487  				new := make([]*Var, len(in))
	 488  				copy(new, out)
	 489  				out = new
	 490  				copied = true
	 491  			}
	 492  			out[i] = w
	 493  		}
	 494  	}
	 495  	return
	 496  }
	 497  
	 498  func (subst *subster) func_(f *Func) *Func {
	 499  	if f != nil {
	 500  		if typ := subst.typ(f.typ); typ != f.typ {
	 501  			copy := *f
	 502  			copy.typ = typ
	 503  			return &copy
	 504  		}
	 505  	}
	 506  	return f
	 507  }
	 508  
	 509  func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
	 510  	out = in
	 511  	for i, f := range in {
	 512  		if g := subst.func_(f); g != f {
	 513  			if !copied {
	 514  				// first function that got substituted => allocate new out slice
	 515  				// and copy all functions
	 516  				new := make([]*Func, len(in))
	 517  				copy(new, out)
	 518  				out = new
	 519  				copied = true
	 520  			}
	 521  			out[i] = g
	 522  		}
	 523  	}
	 524  	return
	 525  }
	 526  
	 527  func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
	 528  	out = in
	 529  	for i, t := range in {
	 530  		if u := subst.typ(t); u != t {
	 531  			if !copied {
	 532  				// first function that got substituted => allocate new out slice
	 533  				// and copy all functions
	 534  				new := make([]Type, len(in))
	 535  				copy(new, out)
	 536  				out = new
	 537  				copied = true
	 538  			}
	 539  			out[i] = u
	 540  		}
	 541  	}
	 542  	return
	 543  }
	 544  

View as plain text