Source file src/cmd/compile/internal/types2/typeparam.go

     1  // Copyright 2011 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  package types2
     6  
     7  import "sync/atomic"
     8  
     9  // Note: This is a uint32 rather than a uint64 because the
    10  // respective 64 bit atomic instructions are not available
    11  // on all platforms.
    12  var lastID atomic.Uint32
    13  
    14  // nextID returns a value increasing monotonically by 1 with
    15  // each call, starting with 1. It may be called concurrently.
    16  func nextID() uint64 { return uint64(lastID.Add(1)) }
    17  
    18  // A TypeParam represents a type parameter type.
    19  type TypeParam struct {
    20  	check *Checker  // for lazy type bound completion
    21  	id    uint64    // unique id, for debugging only
    22  	obj   *TypeName // corresponding type name
    23  	index int       // type parameter index in source order, starting at 0
    24  	bound Type      // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
    25  }
    26  
    27  // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
    28  // type by calling SetTypeParams. Setting a type parameter on more than one type
    29  // will result in a panic.
    30  //
    31  // The constraint argument can be nil, and set later via SetConstraint. If the
    32  // constraint is non-nil, it must be fully defined.
    33  func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
    34  	return (*Checker)(nil).newTypeParam(obj, constraint)
    35  }
    36  
    37  // check may be nil
    38  func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
    39  	// Always increment lastID, even if it is not used.
    40  	id := nextID()
    41  	if check != nil {
    42  		check.nextID++
    43  		id = check.nextID
    44  	}
    45  	typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint}
    46  	if obj.typ == nil {
    47  		obj.typ = typ
    48  	}
    49  	// iface may mutate typ.bound, so we must ensure that iface() is called
    50  	// at least once before the resulting TypeParam escapes.
    51  	if check != nil {
    52  		check.needsCleanup(typ)
    53  	} else if constraint != nil {
    54  		typ.iface()
    55  	}
    56  	return typ
    57  }
    58  
    59  // Obj returns the type name for the type parameter t.
    60  func (t *TypeParam) Obj() *TypeName { return t.obj }
    61  
    62  // Index returns the index of the type param within its param list, or -1 if
    63  // the type parameter has not yet been bound to a type.
    64  func (t *TypeParam) Index() int {
    65  	return t.index
    66  }
    67  
    68  // Constraint returns the type constraint specified for t.
    69  func (t *TypeParam) Constraint() Type {
    70  	return t.bound
    71  }
    72  
    73  // SetConstraint sets the type constraint for t.
    74  //
    75  // It must be called by users of NewTypeParam after the bound's underlying is
    76  // fully defined, and before using the type parameter in any way other than to
    77  // form other types. Once SetConstraint returns the receiver, t is safe for
    78  // concurrent use.
    79  func (t *TypeParam) SetConstraint(bound Type) {
    80  	if bound == nil {
    81  		panic("nil constraint")
    82  	}
    83  	t.bound = bound
    84  	// iface may mutate t.bound (if bound is not an interface), so ensure that
    85  	// this is done before returning.
    86  	t.iface()
    87  }
    88  
    89  // Underlying returns the [underlying type] of the type parameter t, which is
    90  // the underlying type of its constraint. This type is always an interface.
    91  //
    92  // [underlying type]: https://go.dev/ref/spec#Underlying_types.
    93  func (t *TypeParam) Underlying() Type {
    94  	return t.iface()
    95  }
    96  
    97  func (t *TypeParam) String() string { return TypeString(t, nil) }
    98  
    99  // ----------------------------------------------------------------------------
   100  // Implementation
   101  
   102  func (t *TypeParam) cleanup() {
   103  	t.iface()
   104  	t.check = nil
   105  }
   106  
   107  // iface returns the constraint interface of t.
   108  func (t *TypeParam) iface() *Interface {
   109  	bound := t.bound
   110  
   111  	// determine constraint interface
   112  	var ityp *Interface
   113  	switch u := under(bound).(type) {
   114  	case *Basic:
   115  		if !isValid(u) {
   116  			// error is reported elsewhere
   117  			return &emptyInterface
   118  		}
   119  	case *Interface:
   120  		if isTypeParam(bound) {
   121  			// error is reported in Checker.collectTypeParams
   122  			return &emptyInterface
   123  		}
   124  		ityp = u
   125  	}
   126  
   127  	// If we don't have an interface, wrap constraint into an implicit interface.
   128  	if ityp == nil {
   129  		ityp = NewInterfaceType(nil, []Type{bound})
   130  		ityp.implicit = true
   131  		t.bound = ityp // update t.bound for next time (optimization)
   132  	}
   133  
   134  	// compute type set if necessary
   135  	if ityp.tset == nil {
   136  		// pos is used for tracing output; start with the type parameter position.
   137  		pos := t.obj.pos
   138  		// use the (original or possibly instantiated) type bound position if we have one
   139  		if n := asNamed(bound); n != nil {
   140  			pos = n.obj.pos
   141  		}
   142  		computeInterfaceTypeSet(t.check, pos, ityp)
   143  	}
   144  
   145  	return ityp
   146  }
   147  
   148  // is calls f with the specific type terms of t's constraint and reports whether
   149  // all calls to f returned true. If there are no specific terms, is
   150  // returns the result of f(nil).
   151  func (t *TypeParam) is(f func(*term) bool) bool {
   152  	return t.iface().typeSet().is(f)
   153  }
   154  
   155  // underIs calls f with the underlying types of the specific type terms
   156  // of t's constraint and reports whether all calls to f returned true.
   157  // If there are no specific terms, underIs returns the result of f(nil).
   158  func (t *TypeParam) underIs(f func(Type) bool) bool {
   159  	return t.iface().typeSet().underIs(f)
   160  }
   161  

View as plain text