...
Run Format

Source file src/go/types/call.go

Documentation: go/types

  // Copyright 2013 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // This file implements typechecking of call and selector expressions.
  
  package types
  
  import (
  	"go/ast"
  	"go/token"
  )
  
  func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
  	check.exprOrType(x, e.Fun)
  
  	switch x.mode {
  	case invalid:
  		check.use(e.Args...)
  		x.mode = invalid
  		x.expr = e
  		return statement
  
  	case typexpr:
  		// conversion
  		T := x.typ
  		x.mode = invalid
  		switch n := len(e.Args); n {
  		case 0:
  			check.errorf(e.Rparen, "missing argument in conversion to %s", T)
  		case 1:
  			check.expr(x, e.Args[0])
  			if x.mode != invalid {
  				check.conversion(x, T)
  			}
  		default:
  			check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
  		}
  		x.expr = e
  		return conversion
  
  	case builtin:
  		id := x.id
  		if !check.builtin(x, e, id) {
  			x.mode = invalid
  		}
  		x.expr = e
  		// a non-constant result implies a function call
  		if x.mode != invalid && x.mode != constant_ {
  			check.hasCallOrRecv = true
  		}
  		return predeclaredFuncs[id].kind
  
  	default:
  		// function/method call
  		sig, _ := x.typ.Underlying().(*Signature)
  		if sig == nil {
  			check.invalidOp(x.pos(), "cannot call non-function %s", x)
  			x.mode = invalid
  			x.expr = e
  			return statement
  		}
  
  		arg, n, _ := unpack(func(x *operand, i int) { check.multiExpr(x, e.Args[i]) }, len(e.Args), false)
  		if arg != nil {
  			check.arguments(x, e, sig, arg, n)
  		} else {
  			x.mode = invalid
  		}
  
  		// determine result
  		switch sig.results.Len() {
  		case 0:
  			x.mode = novalue
  		case 1:
  			x.mode = value
  			x.typ = sig.results.vars[0].typ // unpack tuple
  		default:
  			x.mode = value
  			x.typ = sig.results
  		}
  
  		x.expr = e
  		check.hasCallOrRecv = true
  
  		return statement
  	}
  }
  
  // use type-checks each argument.
  // Useful to make sure expressions are evaluated
  // (and variables are "used") in the presence of other errors.
  func (check *Checker) use(arg ...ast.Expr) {
  	var x operand
  	for _, e := range arg {
  		if e != nil { // be safe
  			check.rawExpr(&x, e, nil)
  		}
  	}
  }
  
  // useGetter is like use, but takes a getter instead of a list of expressions.
  // It should be called instead of use if a getter is present to avoid repeated
  // evaluation of the first argument (since the getter was likely obtained via
  // unpack, which may have evaluated the first argument already).
  func (check *Checker) useGetter(get getter, n int) {
  	var x operand
  	for i := 0; i < n; i++ {
  		get(&x, i)
  	}
  }
  
  // A getter sets x as the i'th operand, where 0 <= i < n and n is the total
  // number of operands (context-specific, and maintained elsewhere). A getter
  // type-checks the i'th operand; the details of the actual check are getter-
  // specific.
  type getter func(x *operand, i int)
  
  // unpack takes a getter get and a number of operands n. If n == 1, unpack
  // calls the incoming getter for the first operand. If that operand is
  // invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
  // function call, or a comma-ok expression and allowCommaOk is set, the result
  // is a new getter and operand count providing access to the function results,
  // or comma-ok values, respectively. The third result value reports if it
  // is indeed the comma-ok case. In all other cases, the incoming getter and
  // operand count are returned unchanged, and the third result value is false.
  //
  // In other words, if there's exactly one operand that - after type-checking
  // by calling get - stands for multiple operands, the resulting getter provides
  // access to those operands instead.
  //
  // If the returned getter is called at most once for a given operand index i
  // (including i == 0), that operand is guaranteed to cause only one call of
  // the incoming getter with that i.
  //
  func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
  	if n == 1 {
  		// possibly result of an n-valued function call or comma,ok value
  		var x0 operand
  		get(&x0, 0)
  		if x0.mode == invalid {
  			return nil, 0, false
  		}
  
  		if t, ok := x0.typ.(*Tuple); ok {
  			// result of an n-valued function call
  			return func(x *operand, i int) {
  				x.mode = value
  				x.expr = x0.expr
  				x.typ = t.At(i).typ
  			}, t.Len(), false
  		}
  
  		if x0.mode == mapindex || x0.mode == commaok {
  			// comma-ok value
  			if allowCommaOk {
  				a := [2]Type{x0.typ, Typ[UntypedBool]}
  				return func(x *operand, i int) {
  					x.mode = value
  					x.expr = x0.expr
  					x.typ = a[i]
  				}, 2, true
  			}
  			x0.mode = value
  		}
  
  		// single value
  		return func(x *operand, i int) {
  			if i != 0 {
  				unreachable()
  			}
  			*x = x0
  		}, 1, false
  	}
  
  	// zero or multiple values
  	return get, n, false
  }
  
  // arguments checks argument passing for the call with the given signature.
  // The arg function provides the operand for the i'th argument.
  func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
  	if call.Ellipsis.IsValid() {
  		// last argument is of the form x...
  		if !sig.variadic {
  			check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
  			check.useGetter(arg, n)
  			return
  		}
  		if len(call.Args) == 1 && n > 1 {
  			// f()... is not permitted if f() is multi-valued
  			check.errorf(call.Ellipsis, "cannot use ... with %d-valued %s", n, call.Args[0])
  			check.useGetter(arg, n)
  			return
  		}
  	}
  
  	// evaluate arguments
  	for i := 0; i < n; i++ {
  		arg(x, i)
  		if x.mode != invalid {
  			var ellipsis token.Pos
  			if i == n-1 && call.Ellipsis.IsValid() {
  				ellipsis = call.Ellipsis
  			}
  			check.argument(call.Fun, sig, i, x, ellipsis)
  		}
  	}
  
  	// check argument count
  	if sig.variadic {
  		// a variadic function accepts an "empty"
  		// last argument: count one extra
  		n++
  	}
  	if n < sig.params.Len() {
  		check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun)
  		// ok to continue
  	}
  }
  
  // argument checks passing of argument x to the i'th parameter of the given signature.
  // If ellipsis is valid, the argument is followed by ... at that position in the call.
  func (check *Checker) argument(fun ast.Expr, sig *Signature, i int, x *operand, ellipsis token.Pos) {
  	check.singleValue(x)
  	if x.mode == invalid {
  		return
  	}
  
  	n := sig.params.Len()
  
  	// determine parameter type
  	var typ Type
  	switch {
  	case i < n:
  		typ = sig.params.vars[i].typ
  	case sig.variadic:
  		typ = sig.params.vars[n-1].typ
  		if debug {
  			if _, ok := typ.(*Slice); !ok {
  				check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
  			}
  		}
  	default:
  		check.errorf(x.pos(), "too many arguments")
  		return
  	}
  
  	if ellipsis.IsValid() {
  		// argument is of the form x... and x is single-valued
  		if i != n-1 {
  			check.errorf(ellipsis, "can only use ... with matching parameter")
  			return
  		}
  		if _, ok := x.typ.Underlying().(*Slice); !ok && x.typ != Typ[UntypedNil] { // see issue #18268
  			check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
  			return
  		}
  	} else if sig.variadic && i >= n-1 {
  		// use the variadic parameter slice's element type
  		typ = typ.(*Slice).elem
  	}
  
  	check.assignment(x, typ, check.sprintf("argument to %s", fun))
  }
  
  func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
  	// these must be declared before the "goto Error" statements
  	var (
  		obj      Object
  		index    []int
  		indirect bool
  	)
  
  	sel := e.Sel.Name
  	// If the identifier refers to a package, handle everything here
  	// so we don't need a "package" mode for operands: package names
  	// can only appear in qualified identifiers which are mapped to
  	// selector expressions.
  	if ident, ok := e.X.(*ast.Ident); ok {
  		_, obj := check.scope.LookupParent(ident.Name, check.pos)
  		if pname, _ := obj.(*PkgName); pname != nil {
  			assert(pname.pkg == check.pkg)
  			check.recordUse(ident, pname)
  			pname.used = true
  			pkg := pname.imported
  			exp := pkg.scope.Lookup(sel)
  			if exp == nil {
  				if !pkg.fake {
  					check.errorf(e.Pos(), "%s not declared by package %s", sel, pkg.name)
  				}
  				goto Error
  			}
  			if !exp.Exported() {
  				check.errorf(e.Pos(), "%s not exported by package %s", sel, pkg.name)
  				// ok to continue
  			}
  			check.recordUse(e.Sel, exp)
  
  			// Simplified version of the code for *ast.Idents:
  			// - imported objects are always fully initialized
  			switch exp := exp.(type) {
  			case *Const:
  				assert(exp.Val() != nil)
  				x.mode = constant_
  				x.typ = exp.typ
  				x.val = exp.val
  			case *TypeName:
  				x.mode = typexpr
  				x.typ = exp.typ
  			case *Var:
  				x.mode = variable
  				x.typ = exp.typ
  			case *Func:
  				x.mode = value
  				x.typ = exp.typ
  			case *Builtin:
  				x.mode = builtin
  				x.typ = exp.typ
  				x.id = exp.id
  			default:
  				check.dump("unexpected object %v", exp)
  				unreachable()
  			}
  			x.expr = e
  			return
  		}
  	}
  
  	check.exprOrType(x, e.X)
  	if x.mode == invalid {
  		goto Error
  	}
  
  	obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
  	if obj == nil {
  		switch {
  		case index != nil:
  			// TODO(gri) should provide actual type where the conflict happens
  			check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
  		case indirect:
  			check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
  		default:
  			check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
  		}
  		goto Error
  	}
  
  	if x.mode == typexpr {
  		// method expression
  		m, _ := obj.(*Func)
  		if m == nil {
  			check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
  			goto Error
  		}
  
  		check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
  
  		// the receiver type becomes the type of the first function
  		// argument of the method expression's function type
  		var params []*Var
  		sig := m.typ.(*Signature)
  		if sig.params != nil {
  			params = sig.params.vars
  		}
  		x.mode = value
  		x.typ = &Signature{
  			params:   NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
  			results:  sig.results,
  			variadic: sig.variadic,
  		}
  
  		check.addDeclDep(m)
  
  	} else {
  		// regular selector
  		switch obj := obj.(type) {
  		case *Var:
  			check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
  			if x.mode == variable || indirect {
  				x.mode = variable
  			} else {
  				x.mode = value
  			}
  			x.typ = obj.typ
  
  		case *Func:
  			// TODO(gri) If we needed to take into account the receiver's
  			// addressability, should we report the type &(x.typ) instead?
  			check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
  
  			if debug {
  				// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
  				typ := x.typ
  				if x.mode == variable {
  					// If typ is not an (unnamed) pointer or an interface,
  					// use *typ instead, because the method set of *typ
  					// includes the methods of typ.
  					// Variables are addressable, so we can always take their
  					// address.
  					if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
  						typ = &Pointer{base: typ}
  					}
  				}
  				// If we created a synthetic pointer type above, we will throw
  				// away the method set computed here after use.
  				// TODO(gri) Method set computation should probably always compute
  				// both, the value and the pointer receiver method set and represent
  				// them in a single structure.
  				// TODO(gri) Consider also using a method set cache for the lifetime
  				// of checker once we rely on MethodSet lookup instead of individual
  				// lookup.
  				mset := NewMethodSet(typ)
  				if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
  					check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
  					check.dump("%s\n", mset)
  					panic("method sets and lookup don't agree")
  				}
  			}
  
  			x.mode = value
  
  			// remove receiver
  			sig := *obj.typ.(*Signature)
  			sig.recv = nil
  			x.typ = &sig
  
  			check.addDeclDep(obj)
  
  		default:
  			unreachable()
  		}
  	}
  
  	// everything went well
  	x.expr = e
  	return
  
  Error:
  	x.mode = invalid
  	x.expr = e
  }
  

View as plain text