golang.org/x/tools@v0.21.0/internal/refactor/inline/falcon.go (about)

     1  // Copyright 2023 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 inline
     6  
     7  // This file defines the callee side of the "fallible constant" analysis.
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/constant"
    13  	"go/format"
    14  	"go/token"
    15  	"go/types"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"golang.org/x/tools/go/types/typeutil"
    20  	"golang.org/x/tools/internal/aliases"
    21  	"golang.org/x/tools/internal/typeparams"
    22  )
    23  
    24  // falconResult is the result of the analysis of the callee.
    25  type falconResult struct {
    26  	Types       []falconType // types for falcon constraint environment
    27  	Constraints []string     // constraints (Go expressions) on values of fallible constants
    28  }
    29  
    30  // A falconType specifies the name and underlying type of a synthetic
    31  // defined type for use in falcon constraints.
    32  //
    33  // Unique types from callee code are bijectively mapped onto falcon
    34  // types so that constraints are independent of callee type
    35  // information but preserve type equivalence classes.
    36  //
    37  // Fresh names are deliberately obscure to avoid shadowing even if a
    38  // callee parameter has a nanme like "int" or "any".
    39  type falconType struct {
    40  	Name string
    41  	Kind types.BasicKind // string/number/bool
    42  }
    43  
    44  // falcon identifies "fallible constant" expressions, which are
    45  // expressions that may fail to compile if one or more of their
    46  // operands is changed from non-constant to constant.
    47  //
    48  // Consider:
    49  //
    50  //	func sub(s string, i, j int) string { return s[i:j] }
    51  //
    52  // If parameters are replaced by constants, the compiler is
    53  // required to perform these additional checks:
    54  //
    55  //   - if i is constant, 0 <= i.
    56  //   - if s and i are constant, i <= len(s).
    57  //   - ditto for j.
    58  //   - if i and j are constant, i <= j.
    59  //
    60  // s[i:j] is thus a "fallible constant" expression dependent on {s, i,
    61  // j}. Each falcon creates a set of conditional constraints across one
    62  // or more parameter variables.
    63  //
    64  //   - When inlining a call such as sub("abc", -1, 2), the parameter i
    65  //     cannot be eliminated by substitution as its argument value is
    66  //     negative.
    67  //
    68  //   - When inlining sub("", 2, 1), all three parameters cannot be
    69  //     simultaneously eliminated by substitution without violating i
    70  //     <= len(s) and j <= len(s), but the parameters i and j could be
    71  //     safely eliminated without s.
    72  //
    73  // Parameters that cannot be eliminated must remain non-constant,
    74  // either in the form of a binding declaration:
    75  //
    76  //	{ var i int = -1; return "abc"[i:2] }
    77  //
    78  // or a parameter of a literalization:
    79  //
    80  //	func (i int) string { return "abc"[i:2] }(-1)
    81  //
    82  // These example expressions are obviously doomed to fail at run
    83  // time, but in realistic cases such expressions are dominated by
    84  // appropriate conditions that make them reachable only when safe:
    85  //
    86  //	if 0 <= i && i <= j && j <= len(s) { _ = s[i:j] }
    87  //
    88  // (In principle a more sophisticated inliner could entirely eliminate
    89  // such unreachable blocks based on the condition being always-false
    90  // for the given parameter substitution, but this is tricky to do safely
    91  // because the type-checker considers only a single configuration.
    92  // Consider: if runtime.GOOS == "linux" { ... }.)
    93  //
    94  // We believe this is an exhaustive list of "fallible constant" operations:
    95  //
    96  //   - switch z { case x: case y } 	// duplicate case values
    97  //   - s[i], s[i:j], s[i:j:k]		// index out of bounds (0 <= i <= j <= k <= len(s))
    98  //   - T{x: 0}				// index out of bounds, duplicate index
    99  //   - x/y, x%y, x/=y, x%=y		// integer division by zero; minint/-1 overflow
   100  //   - x+y, x-y, x*y			// arithmetic overflow
   101  //   - x<<y				// shift out of range
   102  //   - -x				// negation of minint
   103  //   - T(x)				// value out of range
   104  //
   105  // The fundamental reason for this elaborate algorithm is that the
   106  // "separate analysis" of callee and caller, as required when running
   107  // in an environment such as unitchecker, means that there is no way
   108  // for us to simply invoke the type checker on the combination of
   109  // caller and callee code, as by the time we analyze the caller, we no
   110  // longer have access to type information for the callee (and, in
   111  // particular, any of its direct dependencies that are not direct
   112  // dependencies of the caller). So, in effect, we are forced to map
   113  // the problem in a neutral (callee-type-independent) constraint
   114  // system that can be verified later.
   115  func falcon(logf func(string, ...any), fset *token.FileSet, params map[*types.Var]*paramInfo, info *types.Info, decl *ast.FuncDecl) falconResult {
   116  
   117  	st := &falconState{
   118  		logf:   logf,
   119  		fset:   fset,
   120  		params: params,
   121  		info:   info,
   122  		decl:   decl,
   123  	}
   124  
   125  	// type mapping
   126  	st.int = st.typename(types.Typ[types.Int])
   127  	st.any = "interface{}" // don't use "any" as it may be shadowed
   128  	for obj, info := range st.params {
   129  		if isBasic(obj.Type(), types.IsConstType) {
   130  			info.FalconType = st.typename(obj.Type())
   131  		}
   132  	}
   133  
   134  	st.stmt(st.decl.Body)
   135  
   136  	return st.result
   137  }
   138  
   139  type falconState struct {
   140  	// inputs
   141  	logf   func(string, ...any)
   142  	fset   *token.FileSet
   143  	params map[*types.Var]*paramInfo
   144  	info   *types.Info
   145  	decl   *ast.FuncDecl
   146  
   147  	// working state
   148  	int       string
   149  	any       string
   150  	typenames typeutil.Map
   151  
   152  	result falconResult
   153  }
   154  
   155  // typename returns the name in the falcon constraint system
   156  // of a given string/number/bool type t. Falcon types are
   157  // specified directly in go/types data structures rather than
   158  // by name, avoiding potential shadowing conflicts with
   159  // confusing parameter names such as "int".
   160  //
   161  // Also, each distinct type (as determined by types.Identical)
   162  // is mapped to a fresh type in the falcon system so that we
   163  // can map the types in the callee code into a neutral form
   164  // that does not depend on imports, allowing us to detect
   165  // potential conflicts such as
   166  //
   167  //	map[any]{T1(1): 0, T2(1): 0}
   168  //
   169  // where T1=T2.
   170  func (st *falconState) typename(t types.Type) string {
   171  	name, ok := st.typenames.At(t).(string)
   172  	if !ok {
   173  		basic := t.Underlying().(*types.Basic)
   174  
   175  		// That dot ۰ is an Arabic zero numeral U+06F0.
   176  		// It is very unlikely to appear in a real program.
   177  		// TODO(adonovan): use a non-heuristic solution.
   178  		name = fmt.Sprintf("%s۰%d", basic, st.typenames.Len())
   179  		st.typenames.Set(t, name)
   180  		st.logf("falcon: emit type %s %s // %q", name, basic, t)
   181  		st.result.Types = append(st.result.Types, falconType{
   182  			Name: name,
   183  			Kind: basic.Kind(),
   184  		})
   185  	}
   186  	return name
   187  }
   188  
   189  // -- constraint emission --
   190  
   191  // emit emits a Go expression that must have a legal type.
   192  // In effect, we let the go/types constant folding algorithm
   193  // do most of the heavy lifting (though it may be hard to
   194  // believe from the complexity of this algorithm!).
   195  func (st *falconState) emit(constraint ast.Expr) {
   196  	var out strings.Builder
   197  	if err := format.Node(&out, st.fset, constraint); err != nil {
   198  		panic(err) // can't happen
   199  	}
   200  	syntax := out.String()
   201  	st.logf("falcon: emit constraint %s", syntax)
   202  	st.result.Constraints = append(st.result.Constraints, syntax)
   203  }
   204  
   205  // emitNonNegative emits an []T{}[index] constraint,
   206  // which ensures index is non-negative if constant.
   207  func (st *falconState) emitNonNegative(index ast.Expr) {
   208  	st.emit(&ast.IndexExpr{
   209  		X: &ast.CompositeLit{
   210  			Type: &ast.ArrayType{
   211  				Elt: makeIdent(st.int),
   212  			},
   213  		},
   214  		Index: index,
   215  	})
   216  }
   217  
   218  // emitMonotonic emits an []T{}[i:j] constraint,
   219  // which ensures i <= j if both are constant.
   220  func (st *falconState) emitMonotonic(i, j ast.Expr) {
   221  	st.emit(&ast.SliceExpr{
   222  		X: &ast.CompositeLit{
   223  			Type: &ast.ArrayType{
   224  				Elt: makeIdent(st.int),
   225  			},
   226  		},
   227  		Low:  i,
   228  		High: j,
   229  	})
   230  }
   231  
   232  // emitUnique emits a T{elem1: 0, ... elemN: 0} constraint,
   233  // which ensures that all constant elems are unique.
   234  // T may be a map, slice, or array depending
   235  // on the desired check semantics.
   236  func (st *falconState) emitUnique(typ ast.Expr, elems []ast.Expr) {
   237  	if len(elems) > 1 {
   238  		var elts []ast.Expr
   239  		for _, elem := range elems {
   240  			elts = append(elts, &ast.KeyValueExpr{
   241  				Key:   elem,
   242  				Value: makeIntLit(0),
   243  			})
   244  		}
   245  		st.emit(&ast.CompositeLit{
   246  			Type: typ,
   247  			Elts: elts,
   248  		})
   249  	}
   250  }
   251  
   252  // -- traversal --
   253  
   254  // The traversal functions scan the callee body for expressions that
   255  // are not constant but would become constant if the parameter vars
   256  // were redeclared as constants, and emits for each one a constraint
   257  // (a Go expression) with the property that it will not type-check
   258  // (using types.CheckExpr) if the particular argument values are
   259  // unsuitable.
   260  //
   261  // These constraints are checked by Inline with the actual
   262  // constant argument values. Violations cause it to reject
   263  // parameters as candidates for substitution.
   264  
   265  func (st *falconState) stmt(s ast.Stmt) {
   266  	ast.Inspect(s, func(n ast.Node) bool {
   267  		switch n := n.(type) {
   268  		case ast.Expr:
   269  			_ = st.expr(n)
   270  			return false // skip usual traversal
   271  
   272  		case *ast.AssignStmt:
   273  			switch n.Tok {
   274  			case token.QUO_ASSIGN, token.REM_ASSIGN:
   275  				// x /= y
   276  				// Possible "integer division by zero"
   277  				// Emit constraint: 1/y.
   278  				_ = st.expr(n.Lhs[0])
   279  				kY := st.expr(n.Rhs[0])
   280  				if kY, ok := kY.(ast.Expr); ok {
   281  					op := token.QUO
   282  					if n.Tok == token.REM_ASSIGN {
   283  						op = token.REM
   284  					}
   285  					st.emit(&ast.BinaryExpr{
   286  						Op: op,
   287  						X:  makeIntLit(1),
   288  						Y:  kY,
   289  					})
   290  				}
   291  				return false // skip usual traversal
   292  			}
   293  
   294  		case *ast.SwitchStmt:
   295  			if n.Init != nil {
   296  				st.stmt(n.Init)
   297  			}
   298  			tBool := types.Type(types.Typ[types.Bool])
   299  			tagType := tBool // default: true
   300  			if n.Tag != nil {
   301  				st.expr(n.Tag)
   302  				tagType = st.info.TypeOf(n.Tag)
   303  			}
   304  
   305  			// Possible "duplicate case value".
   306  			// Emit constraint map[T]int{v1: 0, ..., vN:0}
   307  			// to ensure all maybe-constant case values are unique
   308  			// (unless switch tag is boolean, which is relaxed).
   309  			var unique []ast.Expr
   310  			for _, clause := range n.Body.List {
   311  				clause := clause.(*ast.CaseClause)
   312  				for _, caseval := range clause.List {
   313  					if k := st.expr(caseval); k != nil {
   314  						unique = append(unique, st.toExpr(k))
   315  					}
   316  				}
   317  				for _, stmt := range clause.Body {
   318  					st.stmt(stmt)
   319  				}
   320  			}
   321  			if unique != nil && !types.Identical(tagType.Underlying(), tBool) {
   322  				tname := st.any
   323  				if !types.IsInterface(tagType) {
   324  					tname = st.typename(tagType)
   325  				}
   326  				t := &ast.MapType{
   327  					Key:   makeIdent(tname),
   328  					Value: makeIdent(st.int),
   329  				}
   330  				st.emitUnique(t, unique)
   331  			}
   332  		}
   333  		return true
   334  	})
   335  }
   336  
   337  // fieldTypes visits the .Type of each field in the list.
   338  func (st *falconState) fieldTypes(fields *ast.FieldList) {
   339  	if fields != nil {
   340  		for _, field := range fields.List {
   341  			_ = st.expr(field.Type)
   342  		}
   343  	}
   344  }
   345  
   346  // expr visits the expression (or type) and returns a
   347  // non-nil result if the expression is constant or would
   348  // become constant if all suitable function parameters were
   349  // redeclared as constants.
   350  //
   351  // If the expression is constant, st.expr returns its type
   352  // and value (types.TypeAndValue). If the expression would
   353  // become constant, st.expr returns an ast.Expr tree whose
   354  // leaves are literals and parameter references, and whose
   355  // interior nodes are operations that may become constant,
   356  // such as -x, x+y, f(x), and T(x). We call these would-be
   357  // constant expressions "fallible constants", since they may
   358  // fail to type-check for some values of x, i, and j. (We
   359  // refer to the non-nil cases collectively as "maybe
   360  // constant", and the nil case as "definitely non-constant".)
   361  //
   362  // As a side effect, st.expr emits constraints for each
   363  // fallible constant expression; this is its main purpose.
   364  //
   365  // Consequently, st.expr must visit the entire subtree so
   366  // that all necessary constraints are emitted. It may not
   367  // short-circuit the traversal when it encounters a constant
   368  // subexpression as constants may contain arbitrary other
   369  // syntax that may impose constraints. Consider (as always)
   370  // this contrived but legal example of a type parameter (!)
   371  // that contains statement syntax:
   372  //
   373  //	func f[T [unsafe.Sizeof(func() { stmts })]int]()
   374  //
   375  // There is no need to emit constraints for (e.g.) s[i] when s
   376  // and i are already constants, because we know the expression
   377  // is sound, but it is sometimes easier to emit these
   378  // redundant constraints than to avoid them.
   379  func (st *falconState) expr(e ast.Expr) (res any) { // = types.TypeAndValue | ast.Expr
   380  	tv := st.info.Types[e]
   381  	if tv.Value != nil {
   382  		// A constant value overrides any other result.
   383  		defer func() { res = tv }()
   384  	}
   385  
   386  	switch e := e.(type) {
   387  	case *ast.Ident:
   388  		if v, ok := st.info.Uses[e].(*types.Var); ok {
   389  			if _, ok := st.params[v]; ok && isBasic(v.Type(), types.IsConstType) {
   390  				return e // reference to constable parameter
   391  			}
   392  		}
   393  		// (References to *types.Const are handled by the defer.)
   394  
   395  	case *ast.BasicLit:
   396  		// constant
   397  
   398  	case *ast.ParenExpr:
   399  		return st.expr(e.X)
   400  
   401  	case *ast.FuncLit:
   402  		_ = st.expr(e.Type)
   403  		st.stmt(e.Body)
   404  		// definitely non-constant
   405  
   406  	case *ast.CompositeLit:
   407  		// T{k: v, ...}, where T ∈ {array,*array,slice,map},
   408  		// imposes a constraint that all constant k are
   409  		// distinct and, for arrays [n]T, within range 0-n.
   410  		//
   411  		// Types matter, not just values. For example,
   412  		// an interface-keyed map may contain keys
   413  		// that are numerically equal so long as they
   414  		// are of distinct types. For example:
   415  		//
   416  		//   type myint int
   417  		//   map[any]bool{1: true, 1:        true} // error: duplicate key
   418  		//   map[any]bool{1: true, int16(1): true} // ok
   419  		//   map[any]bool{1: true, myint(1): true} // ok
   420  		//
   421  		// This can be asserted by emitting a
   422  		// constraint of the form T{k1: 0, ..., kN: 0}.
   423  		if e.Type != nil {
   424  			_ = st.expr(e.Type)
   425  		}
   426  		t := aliases.Unalias(typeparams.Deref(tv.Type))
   427  		var uniques []ast.Expr
   428  		for _, elt := range e.Elts {
   429  			if kv, ok := elt.(*ast.KeyValueExpr); ok {
   430  				if !is[*types.Struct](t) {
   431  					if k := st.expr(kv.Key); k != nil {
   432  						uniques = append(uniques, st.toExpr(k))
   433  					}
   434  				}
   435  				_ = st.expr(kv.Value)
   436  			} else {
   437  				_ = st.expr(elt)
   438  			}
   439  		}
   440  		if uniques != nil {
   441  			// Inv: not a struct.
   442  
   443  			// The type T in constraint T{...} depends on the CompLit:
   444  			// - for a basic-keyed map, use map[K]int;
   445  			// - for an interface-keyed map, use map[any]int;
   446  			// - for a slice, use []int;
   447  			// - for an array or *array, use [n]int.
   448  			// The last two entail progressively stronger index checks.
   449  			var ct ast.Expr // type syntax for constraint
   450  			switch t := t.(type) {
   451  			case *types.Map:
   452  				if types.IsInterface(t.Key()) {
   453  					ct = &ast.MapType{
   454  						Key:   makeIdent(st.any),
   455  						Value: makeIdent(st.int),
   456  					}
   457  				} else {
   458  					ct = &ast.MapType{
   459  						Key:   makeIdent(st.typename(t.Key())),
   460  						Value: makeIdent(st.int),
   461  					}
   462  				}
   463  			case *types.Array: // or *array
   464  				ct = &ast.ArrayType{
   465  					Len: makeIntLit(t.Len()),
   466  					Elt: makeIdent(st.int),
   467  				}
   468  			default:
   469  				panic(t)
   470  			}
   471  			st.emitUnique(ct, uniques)
   472  		}
   473  		// definitely non-constant
   474  
   475  	case *ast.SelectorExpr:
   476  		_ = st.expr(e.X)
   477  		_ = st.expr(e.Sel)
   478  		// The defer is sufficient to handle
   479  		// qualified identifiers (pkg.Const).
   480  		// All other cases are definitely non-constant.
   481  
   482  	case *ast.IndexExpr:
   483  		if tv.IsType() {
   484  			// type C[T]
   485  			_ = st.expr(e.X)
   486  			_ = st.expr(e.Index)
   487  		} else {
   488  			// term x[i]
   489  			//
   490  			// Constraints (if x is slice/string/array/*array, not map):
   491  			// - i >= 0
   492  			//     if i is a fallible constant
   493  			// - i < len(x)
   494  			//     if x is array/*array and
   495  			//     i is a fallible constant;
   496  			//  or if s is a string and both i,
   497  			//     s are maybe-constants,
   498  			//     but not both are constants.
   499  			kX := st.expr(e.X)
   500  			kI := st.expr(e.Index)
   501  			if kI != nil && !is[*types.Map](st.info.TypeOf(e.X).Underlying()) {
   502  				if kI, ok := kI.(ast.Expr); ok {
   503  					st.emitNonNegative(kI)
   504  				}
   505  				// Emit constraint to check indices against known length.
   506  				// TODO(adonovan): factor with SliceExpr logic.
   507  				var x ast.Expr
   508  				if kX != nil {
   509  					// string
   510  					x = st.toExpr(kX)
   511  				} else if arr, ok := typeparams.CoreType(typeparams.Deref(st.info.TypeOf(e.X))).(*types.Array); ok {
   512  					// array, *array
   513  					x = &ast.CompositeLit{
   514  						Type: &ast.ArrayType{
   515  							Len: makeIntLit(arr.Len()),
   516  							Elt: makeIdent(st.int),
   517  						},
   518  					}
   519  				}
   520  				if x != nil {
   521  					st.emit(&ast.IndexExpr{
   522  						X:     x,
   523  						Index: st.toExpr(kI),
   524  					})
   525  				}
   526  			}
   527  		}
   528  		// definitely non-constant
   529  
   530  	case *ast.SliceExpr:
   531  		// x[low:high:max]
   532  		//
   533  		// Emit non-negative constraints for each index,
   534  		// plus low <= high <= max <= len(x)
   535  		// for each pair that are maybe-constant
   536  		// but not definitely constant.
   537  
   538  		kX := st.expr(e.X)
   539  		var kLow, kHigh, kMax any
   540  		if e.Low != nil {
   541  			kLow = st.expr(e.Low)
   542  			if kLow != nil {
   543  				if kLow, ok := kLow.(ast.Expr); ok {
   544  					st.emitNonNegative(kLow)
   545  				}
   546  			}
   547  		}
   548  		if e.High != nil {
   549  			kHigh = st.expr(e.High)
   550  			if kHigh != nil {
   551  				if kHigh, ok := kHigh.(ast.Expr); ok {
   552  					st.emitNonNegative(kHigh)
   553  				}
   554  				if kLow != nil {
   555  					st.emitMonotonic(st.toExpr(kLow), st.toExpr(kHigh))
   556  				}
   557  			}
   558  		}
   559  		if e.Max != nil {
   560  			kMax = st.expr(e.Max)
   561  			if kMax != nil {
   562  				if kMax, ok := kMax.(ast.Expr); ok {
   563  					st.emitNonNegative(kMax)
   564  				}
   565  				if kHigh != nil {
   566  					st.emitMonotonic(st.toExpr(kHigh), st.toExpr(kMax))
   567  				}
   568  			}
   569  		}
   570  
   571  		// Emit constraint to check indices against known length.
   572  		var x ast.Expr
   573  		if kX != nil {
   574  			// string
   575  			x = st.toExpr(kX)
   576  		} else if arr, ok := typeparams.CoreType(typeparams.Deref(st.info.TypeOf(e.X))).(*types.Array); ok {
   577  			// array, *array
   578  			x = &ast.CompositeLit{
   579  				Type: &ast.ArrayType{
   580  					Len: makeIntLit(arr.Len()),
   581  					Elt: makeIdent(st.int),
   582  				},
   583  			}
   584  		}
   585  		if x != nil {
   586  			// Avoid slice[::max] if kHigh is nonconstant (nil).
   587  			high, max := st.toExpr(kHigh), st.toExpr(kMax)
   588  			if high == nil {
   589  				high = max // => slice[:max:max]
   590  			}
   591  			st.emit(&ast.SliceExpr{
   592  				X:    x,
   593  				Low:  st.toExpr(kLow),
   594  				High: high,
   595  				Max:  max,
   596  			})
   597  		}
   598  		// definitely non-constant
   599  
   600  	case *ast.TypeAssertExpr:
   601  		_ = st.expr(e.X)
   602  		if e.Type != nil {
   603  			_ = st.expr(e.Type)
   604  		}
   605  
   606  	case *ast.CallExpr:
   607  		_ = st.expr(e.Fun)
   608  		if tv, ok := st.info.Types[e.Fun]; ok && tv.IsType() {
   609  			// conversion T(x)
   610  			//
   611  			// Possible "value out of range".
   612  			kX := st.expr(e.Args[0])
   613  			if kX != nil && isBasic(tv.Type, types.IsConstType) {
   614  				conv := convert(makeIdent(st.typename(tv.Type)), st.toExpr(kX))
   615  				if is[ast.Expr](kX) {
   616  					st.emit(conv)
   617  				}
   618  				return conv
   619  			}
   620  			return nil // definitely non-constant
   621  		}
   622  
   623  		// call f(x)
   624  
   625  		all := true // all args are possibly-constant
   626  		kArgs := make([]ast.Expr, len(e.Args))
   627  		for i, arg := range e.Args {
   628  			if kArg := st.expr(arg); kArg != nil {
   629  				kArgs[i] = st.toExpr(kArg)
   630  			} else {
   631  				all = false
   632  			}
   633  		}
   634  
   635  		// Calls to built-ins with fallibly constant arguments
   636  		// may become constant. All other calls are either
   637  		// constant or non-constant
   638  		if id, ok := e.Fun.(*ast.Ident); ok && all && tv.Value == nil {
   639  			if builtin, ok := st.info.Uses[id].(*types.Builtin); ok {
   640  				switch builtin.Name() {
   641  				case "len", "imag", "real", "complex", "min", "max":
   642  					return &ast.CallExpr{
   643  						Fun:      id,
   644  						Args:     kArgs,
   645  						Ellipsis: e.Ellipsis,
   646  					}
   647  				}
   648  			}
   649  		}
   650  
   651  	case *ast.StarExpr: // *T, *ptr
   652  		_ = st.expr(e.X)
   653  
   654  	case *ast.UnaryExpr:
   655  		// + - ! ^ & <- ~
   656  		//
   657  		// Possible "negation of minint".
   658  		// Emit constraint: -x
   659  		kX := st.expr(e.X)
   660  		if kX != nil && !is[types.TypeAndValue](kX) {
   661  			if e.Op == token.SUB {
   662  				st.emit(&ast.UnaryExpr{
   663  					Op: e.Op,
   664  					X:  st.toExpr(kX),
   665  				})
   666  			}
   667  
   668  			return &ast.UnaryExpr{
   669  				Op: e.Op,
   670  				X:  st.toExpr(kX),
   671  			}
   672  		}
   673  
   674  	case *ast.BinaryExpr:
   675  		kX := st.expr(e.X)
   676  		kY := st.expr(e.Y)
   677  		switch e.Op {
   678  		case token.QUO, token.REM:
   679  			// x/y, x%y
   680  			//
   681  			// Possible "integer division by zero" or
   682  			// "minint / -1" overflow.
   683  			// Emit constraint: x/y or 1/y
   684  			if kY != nil {
   685  				if kX == nil {
   686  					kX = makeIntLit(1)
   687  				}
   688  				st.emit(&ast.BinaryExpr{
   689  					Op: e.Op,
   690  					X:  st.toExpr(kX),
   691  					Y:  st.toExpr(kY),
   692  				})
   693  			}
   694  
   695  		case token.ADD, token.SUB, token.MUL:
   696  			// x+y, x-y, x*y
   697  			//
   698  			// Possible "arithmetic overflow".
   699  			// Emit constraint: x+y
   700  			if kX != nil && kY != nil {
   701  				st.emit(&ast.BinaryExpr{
   702  					Op: e.Op,
   703  					X:  st.toExpr(kX),
   704  					Y:  st.toExpr(kY),
   705  				})
   706  			}
   707  
   708  		case token.SHL, token.SHR:
   709  			// x << y, x >> y
   710  			//
   711  			// Possible "constant shift too large".
   712  			// Either operand may be too large individually,
   713  			// and they may be too large together.
   714  			// Emit constraint:
   715  			//    x << y (if both maybe-constant)
   716  			//    x << 0 (if y is non-constant)
   717  			//    1 << y (if x is non-constant)
   718  			if kX != nil || kY != nil {
   719  				x := st.toExpr(kX)
   720  				if x == nil {
   721  					x = makeIntLit(1)
   722  				}
   723  				y := st.toExpr(kY)
   724  				if y == nil {
   725  					y = makeIntLit(0)
   726  				}
   727  				st.emit(&ast.BinaryExpr{
   728  					Op: e.Op,
   729  					X:  x,
   730  					Y:  y,
   731  				})
   732  			}
   733  
   734  		case token.LSS, token.GTR, token.EQL, token.NEQ, token.LEQ, token.GEQ:
   735  			// < > == != <= <=
   736  			//
   737  			// A "x cmp y" expression with constant operands x, y is
   738  			// itself constant, but I can't see how a constant bool
   739  			// could be fallible: the compiler doesn't reject duplicate
   740  			// boolean cases in a switch, presumably because boolean
   741  			// switches are less like n-way branches and more like
   742  			// sequential if-else chains with possibly overlapping
   743  			// conditions; and there is (sadly) no way to convert a
   744  			// boolean constant to an int constant.
   745  		}
   746  		if kX != nil && kY != nil {
   747  			return &ast.BinaryExpr{
   748  				Op: e.Op,
   749  				X:  st.toExpr(kX),
   750  				Y:  st.toExpr(kY),
   751  			}
   752  		}
   753  
   754  	// types
   755  	//
   756  	// We need to visit types (and even type parameters)
   757  	// in order to reach all the places where things could go wrong:
   758  	//
   759  	// 	const (
   760  	// 		s = ""
   761  	// 		i = 0
   762  	// 	)
   763  	// 	type C[T [unsafe.Sizeof(func() { _ = s[i] })]int] bool
   764  
   765  	case *ast.IndexListExpr:
   766  		_ = st.expr(e.X)
   767  		for _, expr := range e.Indices {
   768  			_ = st.expr(expr)
   769  		}
   770  
   771  	case *ast.Ellipsis:
   772  		if e.Elt != nil {
   773  			_ = st.expr(e.Elt)
   774  		}
   775  
   776  	case *ast.ArrayType:
   777  		if e.Len != nil {
   778  			_ = st.expr(e.Len)
   779  		}
   780  		_ = st.expr(e.Elt)
   781  
   782  	case *ast.StructType:
   783  		st.fieldTypes(e.Fields)
   784  
   785  	case *ast.FuncType:
   786  		st.fieldTypes(e.TypeParams)
   787  		st.fieldTypes(e.Params)
   788  		st.fieldTypes(e.Results)
   789  
   790  	case *ast.InterfaceType:
   791  		st.fieldTypes(e.Methods)
   792  
   793  	case *ast.MapType:
   794  		_ = st.expr(e.Key)
   795  		_ = st.expr(e.Value)
   796  
   797  	case *ast.ChanType:
   798  		_ = st.expr(e.Value)
   799  	}
   800  	return
   801  }
   802  
   803  // toExpr converts the result of visitExpr to a falcon expression.
   804  // (We don't do this in visitExpr as we first need to discriminate
   805  // constants from maybe-constants.)
   806  func (st *falconState) toExpr(x any) ast.Expr {
   807  	switch x := x.(type) {
   808  	case nil:
   809  		return nil
   810  
   811  	case types.TypeAndValue:
   812  		lit := makeLiteral(x.Value)
   813  		if !isBasic(x.Type, types.IsUntyped) {
   814  			// convert to "typed" type
   815  			lit = &ast.CallExpr{
   816  				Fun:  makeIdent(st.typename(x.Type)),
   817  				Args: []ast.Expr{lit},
   818  			}
   819  		}
   820  		return lit
   821  
   822  	case ast.Expr:
   823  		return x
   824  
   825  	default:
   826  		panic(x)
   827  	}
   828  }
   829  
   830  func makeLiteral(v constant.Value) ast.Expr {
   831  	switch v.Kind() {
   832  	case constant.Bool:
   833  		// Rather than refer to the true or false built-ins,
   834  		// which could be shadowed by poorly chosen parameter
   835  		// names, we use 0 == 0 for true and 0 != 0 for false.
   836  		op := token.EQL
   837  		if !constant.BoolVal(v) {
   838  			op = token.NEQ
   839  		}
   840  		return &ast.BinaryExpr{
   841  			Op: op,
   842  			X:  makeIntLit(0),
   843  			Y:  makeIntLit(0),
   844  		}
   845  
   846  	case constant.String:
   847  		return &ast.BasicLit{
   848  			Kind:  token.STRING,
   849  			Value: v.ExactString(),
   850  		}
   851  
   852  	case constant.Int:
   853  		return &ast.BasicLit{
   854  			Kind:  token.INT,
   855  			Value: v.ExactString(),
   856  		}
   857  
   858  	case constant.Float:
   859  		return &ast.BasicLit{
   860  			Kind:  token.FLOAT,
   861  			Value: v.ExactString(),
   862  		}
   863  
   864  	case constant.Complex:
   865  		// The components could be float or int.
   866  		y := makeLiteral(constant.Imag(v))
   867  		y.(*ast.BasicLit).Value += "i" // ugh
   868  		if re := constant.Real(v); !consteq(re, kZeroInt) {
   869  			// complex: x + yi
   870  			y = &ast.BinaryExpr{
   871  				Op: token.ADD,
   872  				X:  makeLiteral(re),
   873  				Y:  y,
   874  			}
   875  		}
   876  		return y
   877  
   878  	default:
   879  		panic(v.Kind())
   880  	}
   881  }
   882  
   883  func makeIntLit(x int64) *ast.BasicLit {
   884  	return &ast.BasicLit{
   885  		Kind:  token.INT,
   886  		Value: strconv.FormatInt(x, 10),
   887  	}
   888  }
   889  
   890  func isBasic(t types.Type, info types.BasicInfo) bool {
   891  	basic, ok := t.Underlying().(*types.Basic)
   892  	return ok && basic.Info()&info != 0
   893  }