union.go raw

   1  // Copyright 2021 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 types
   6  
   7  import (
   8  	"go/ast"
   9  	"go/token"
  10  	. "internal/types/errors"
  11  )
  12  
  13  // ----------------------------------------------------------------------------
  14  // API
  15  
  16  // A Union represents a union of terms embedded in an interface.
  17  type Union struct {
  18  	terms []*Term // list of syntactical terms (not a canonicalized termlist)
  19  }
  20  
  21  // NewUnion returns a new [Union] type with the given terms.
  22  // It is an error to create an empty union; they are syntactically not possible.
  23  func NewUnion(terms []*Term) *Union {
  24  	if len(terms) == 0 {
  25  		panic("empty union")
  26  	}
  27  	return &Union{terms}
  28  }
  29  
  30  func (u *Union) Len() int         { return len(u.terms) }
  31  func (u *Union) Term(i int) *Term { return u.terms[i] }
  32  
  33  func (u *Union) Underlying() Type { return u }
  34  func (u *Union) String() string   { return TypeString(u, nil) }
  35  
  36  // A Term represents a term in a [Union].
  37  type Term term
  38  
  39  // NewTerm returns a new union term.
  40  func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} }
  41  
  42  func (t *Term) Tilde() bool    { return t.tilde }
  43  func (t *Term) Type() Type     { return t.typ }
  44  func (t *Term) String() string { return (*term)(t).String() }
  45  
  46  // ----------------------------------------------------------------------------
  47  // Implementation
  48  
  49  // Avoid excessive type-checking times due to quadratic termlist operations.
  50  const maxTermCount = 100
  51  
  52  // parseUnion parses uexpr as a union of expressions.
  53  // The result is a Union type, or Typ[Invalid] for some errors.
  54  func parseUnion(check *Checker, uexpr ast.Expr) Type {
  55  	blist, tlist := flattenUnion(nil, uexpr)
  56  	assert(len(blist) == len(tlist)-1)
  57  
  58  	var terms []*Term
  59  
  60  	var u Type
  61  	for i, x := range tlist {
  62  		term := parseTilde(check, x)
  63  		if len(tlist) == 1 && !term.tilde {
  64  			// Single type. Ok to return early because all relevant
  65  			// checks have been performed in parseTilde (no need to
  66  			// run through term validity check below).
  67  			return term.typ // typ already recorded through check.typ in parseTilde
  68  		}
  69  		if len(terms) >= maxTermCount {
  70  			if isValid(u) {
  71  				check.errorf(x, InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
  72  				u = Typ[Invalid]
  73  			}
  74  		} else {
  75  			terms = append(terms, term)
  76  			u = &Union{terms}
  77  		}
  78  
  79  		if i > 0 {
  80  			check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
  81  		}
  82  	}
  83  
  84  	if !isValid(u) {
  85  		return u
  86  	}
  87  
  88  	// Check validity of terms.
  89  	// Do this check later because it requires types to be set up.
  90  	// Note: This is a quadratic algorithm, but unions tend to be short.
  91  	check.later(func() {
  92  		for i, t := range terms {
  93  			if !isValid(t.typ) {
  94  				continue
  95  			}
  96  
  97  			u := under(t.typ)
  98  			f, _ := u.(*Interface)
  99  			if t.tilde {
 100  				if f != nil {
 101  					check.errorf(tlist[i], InvalidUnion, "invalid use of ~ (%s is an interface)", t.typ)
 102  					continue // don't report another error for t
 103  				}
 104  
 105  				if !Identical(u, t.typ) {
 106  					check.errorf(tlist[i], InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
 107  					continue
 108  				}
 109  			}
 110  
 111  			// Stand-alone embedded interfaces are ok and are handled by the single-type case
 112  			// in the beginning. Embedded interfaces with tilde are excluded above. If we reach
 113  			// here, we must have at least two terms in the syntactic term list (but not necessarily
 114  			// in the term list of the union's type set).
 115  			if f != nil {
 116  				tset := f.typeSet()
 117  				switch {
 118  				case tset.NumMethods() != 0:
 119  					check.errorf(tlist[i], InvalidUnion, "cannot use %s in union (%s contains methods)", t, t)
 120  				case t.typ == universeComparable.Type():
 121  					check.error(tlist[i], InvalidUnion, "cannot use comparable in union")
 122  				case tset.comparable:
 123  					check.errorf(tlist[i], InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t)
 124  				}
 125  				continue // terms with interface types are not subject to the no-overlap rule
 126  			}
 127  
 128  			// Report overlapping (non-disjoint) terms such as
 129  			// a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a).
 130  			if j := overlappingTerm(terms[:i], t); j >= 0 {
 131  				check.softErrorf(tlist[i], InvalidUnion, "overlapping terms %s and %s", t, terms[j])
 132  			}
 133  		}
 134  	}).describef(uexpr, "check term validity %s", uexpr)
 135  
 136  	return u
 137  }
 138  
 139  func parseTilde(check *Checker, tx ast.Expr) *Term {
 140  	x := tx
 141  	var tilde bool
 142  	if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
 143  		x = op.X
 144  		tilde = true
 145  	}
 146  	typ := check.typ(x)
 147  	// Embedding stand-alone type parameters is not permitted (go.dev/issue/47127).
 148  	// We don't need this restriction anymore if we make the underlying type of a type
 149  	// parameter its constraint interface: if we embed a lone type parameter, we will
 150  	// simply use its underlying type (like we do for other named, embedded interfaces),
 151  	// and since the underlying type is an interface the embedding is well defined.
 152  	if isTypeParam(typ) {
 153  		if tilde {
 154  			check.errorf(x, MisplacedTypeParam, "type in term %s cannot be a type parameter", tx)
 155  		} else {
 156  			check.error(x, MisplacedTypeParam, "term cannot be a type parameter")
 157  		}
 158  		typ = Typ[Invalid]
 159  	}
 160  	term := NewTerm(tilde, typ)
 161  	if tilde {
 162  		check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil)
 163  	}
 164  	return term
 165  }
 166  
 167  // overlappingTerm reports the index of the term x in terms which is
 168  // overlapping (not disjoint) from y. The result is < 0 if there is no
 169  // such term. The type of term y must not be an interface, and terms
 170  // with an interface type are ignored in the terms list.
 171  func overlappingTerm(terms []*Term, y *Term) int {
 172  	assert(!IsInterface(y.typ))
 173  	for i, x := range terms {
 174  		if IsInterface(x.typ) {
 175  			continue
 176  		}
 177  		// disjoint requires non-nil, non-top arguments,
 178  		// and non-interface types as term types.
 179  		if debug {
 180  			if x == nil || x.typ == nil || y == nil || y.typ == nil {
 181  				panic("empty or top union term")
 182  			}
 183  		}
 184  		if !(*term)(x).disjoint((*term)(y)) {
 185  			return i
 186  		}
 187  	}
 188  	return -1
 189  }
 190  
 191  // flattenUnion walks a union type expression of the form A | B | C | ...,
 192  // extracting both the binary exprs (blist) and leaf types (tlist).
 193  func flattenUnion(list []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) {
 194  	if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
 195  		blist, tlist = flattenUnion(list, o.X)
 196  		blist = append(blist, o)
 197  		x = o.Y
 198  	}
 199  	return blist, append(tlist, x)
 200  }
 201