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

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

View as plain text