...

Source file src/golang.org/x/tools/go/ssa/coretype.go

Documentation: golang.org/x/tools/go/ssa

     1  // Copyright 2022 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 ssa
     6  
     7  import (
     8  	"go/types"
     9  
    10  	"golang.org/x/tools/internal/typeparams"
    11  )
    12  
    13  // Utilities for dealing with core types.
    14  
    15  // isBytestring returns true if T has the same terms as interface{[]byte | string}.
    16  // These act like a core type for some operations: slice expressions, append and copy.
    17  //
    18  // See https://go.dev/ref/spec#Core_types for the details on bytestring.
    19  func isBytestring(T types.Type) bool {
    20  	U := T.Underlying()
    21  	if _, ok := U.(*types.Interface); !ok {
    22  		return false
    23  	}
    24  
    25  	tset := typeSetOf(U)
    26  	if tset.Len() != 2 {
    27  		return false
    28  	}
    29  	hasBytes, hasString := false, false
    30  	underIs(tset, func(t types.Type) bool {
    31  		switch {
    32  		case isString(t):
    33  			hasString = true
    34  		case isByteSlice(t):
    35  			hasBytes = true
    36  		}
    37  		return hasBytes || hasString
    38  	})
    39  	return hasBytes && hasString
    40  }
    41  
    42  // termList is a list of types.
    43  type termList []*typeparams.Term       // type terms of the type set
    44  func (s termList) Len() int            { return len(s) }
    45  func (s termList) At(i int) types.Type { return s[i].Type() }
    46  
    47  // typeSetOf returns the type set of typ. Returns an empty typeset on an error.
    48  func typeSetOf(typ types.Type) termList {
    49  	// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
    50  	var terms []*typeparams.Term
    51  	var err error
    52  	switch typ := typ.(type) {
    53  	case *typeparams.TypeParam:
    54  		terms, err = typeparams.StructuralTerms(typ)
    55  	case *typeparams.Union:
    56  		terms, err = typeparams.UnionTermSet(typ)
    57  	case *types.Interface:
    58  		terms, err = typeparams.InterfaceTermSet(typ)
    59  	default:
    60  		// Common case.
    61  		// Specializing the len=1 case to avoid a slice
    62  		// had no measurable space/time benefit.
    63  		terms = []*typeparams.Term{typeparams.NewTerm(false, typ)}
    64  	}
    65  
    66  	if err != nil {
    67  		return termList(nil)
    68  	}
    69  	return termList(terms)
    70  }
    71  
    72  // underIs calls f with the underlying types of the specific type terms
    73  // of s and reports whether all calls to f returned true. If there are
    74  // no specific terms, underIs returns the result of f(nil).
    75  func underIs(s termList, f func(types.Type) bool) bool {
    76  	if s.Len() == 0 {
    77  		return f(nil)
    78  	}
    79  	for i := 0; i < s.Len(); i++ {
    80  		u := s.At(i).Underlying()
    81  		if !f(u) {
    82  			return false
    83  		}
    84  	}
    85  	return true
    86  }
    87  
    88  // indexType returns the element type and index mode of a IndexExpr over a type.
    89  // It returns (nil, invalid) if the type is not indexable; this should never occur in a well-typed program.
    90  func indexType(typ types.Type) (types.Type, indexMode) {
    91  	switch U := typ.Underlying().(type) {
    92  	case *types.Array:
    93  		return U.Elem(), ixArrVar
    94  	case *types.Pointer:
    95  		if arr, ok := U.Elem().Underlying().(*types.Array); ok {
    96  			return arr.Elem(), ixVar
    97  		}
    98  	case *types.Slice:
    99  		return U.Elem(), ixVar
   100  	case *types.Map:
   101  		return U.Elem(), ixMap
   102  	case *types.Basic:
   103  		return tByte, ixValue // must be a string
   104  	case *types.Interface:
   105  		tset := typeSetOf(U)
   106  		if tset.Len() == 0 {
   107  			return nil, ixInvalid // no underlying terms or error is empty.
   108  		}
   109  
   110  		elem, mode := indexType(tset.At(0))
   111  		for i := 1; i < tset.Len() && mode != ixInvalid; i++ {
   112  			e, m := indexType(tset.At(i))
   113  			if !types.Identical(elem, e) { // if type checked, just a sanity check
   114  				return nil, ixInvalid
   115  			}
   116  			// Update the mode to the most constrained address type.
   117  			mode = mode.meet(m)
   118  		}
   119  		if mode != ixInvalid {
   120  			return elem, mode
   121  		}
   122  	}
   123  	return nil, ixInvalid
   124  }
   125  
   126  // An indexMode specifies the (addressing) mode of an index operand.
   127  //
   128  // Addressing mode of an index operation is based on the set of
   129  // underlying types.
   130  // Hasse diagram of the indexMode meet semi-lattice:
   131  //
   132  //	ixVar     ixMap
   133  //	  |          |
   134  //	ixArrVar     |
   135  //	  |          |
   136  //	ixValue      |
   137  //	   \        /
   138  //	  ixInvalid
   139  type indexMode byte
   140  
   141  const (
   142  	ixInvalid indexMode = iota // index is invalid
   143  	ixValue                    // index is a computed value (not addressable)
   144  	ixArrVar                   // like ixVar, but index operand contains an array
   145  	ixVar                      // index is an addressable variable
   146  	ixMap                      // index is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
   147  )
   148  
   149  // meet is the address type that is constrained by both x and y.
   150  func (x indexMode) meet(y indexMode) indexMode {
   151  	if (x == ixMap || y == ixMap) && x != y {
   152  		return ixInvalid
   153  	}
   154  	// Use int representation and return min.
   155  	if x < y {
   156  		return y
   157  	}
   158  	return x
   159  }
   160  

View as plain text