blob: 7dec65c1d6875fd54f305557bd7cca485e5da9f8 [file] [log] [blame]
// Copyright 2009 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.
package typecheck
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
"go/constant"
"go/token"
)
// MakeDotArgs package all the arguments that match a ... T parameter into a []T.
func MakeDotArgs(pos src.XPos, typ *types.Type, args []ir.Node) ir.Node {
var n ir.Node
if len(args) == 0 {
n = ir.NewNilExpr(pos)
n.SetType(typ)
} else {
args = append([]ir.Node(nil), args...)
lit := ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), args)
lit.SetImplicit(true)
n = lit
}
n = Expr(n)
if n.Type() == nil {
base.FatalfAt(pos, "mkdotargslice: typecheck failed")
}
return n
}
// FixVariadicCall rewrites calls to variadic functions to use an
// explicit ... argument if one is not already present.
func FixVariadicCall(call *ir.CallExpr) {
fntype := call.X.Type()
if !fntype.IsVariadic() || call.IsDDD {
return
}
vi := fntype.NumParams() - 1
vt := fntype.Params().Field(vi).Type
args := call.Args
extra := args[vi:]
slice := MakeDotArgs(call.Pos(), vt, extra)
for i := range extra {
extra[i] = nil // allow GC
}
call.Args = append(args[:vi], slice)
call.IsDDD = true
}
// FixMethodCall rewrites a method call t.M(...) into a function call T.M(t, ...).
func FixMethodCall(call *ir.CallExpr) {
if call.X.Op() != ir.ODOTMETH {
return
}
dot := call.X.(*ir.SelectorExpr)
fn := Expr(ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym))
args := make([]ir.Node, 1+len(call.Args))
args[0] = dot.X
copy(args[1:], call.Args)
call.SetOp(ir.OCALLFUNC)
call.X = fn
call.Args = args
}
// ClosureType returns the struct type used to hold all the information
// needed in the closure for clo (clo must be a OCLOSURE node).
// The address of a variable of the returned type can be cast to a func.
func ClosureType(clo *ir.ClosureExpr) *types.Type {
// Create closure in the form of a composite literal.
// supposing the closure captures an int i and a string s
// and has one float64 argument and no results,
// the generated code looks like:
//
// clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
//
// The use of the struct provides type information to the garbage
// collector so that it can walk the closure. We could use (in this case)
// [3]unsafe.Pointer instead, but that would leave the gc in the dark.
// The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor.
// Make sure the .F field is in the same package as the rest of the
// fields. This deals with closures in instantiated functions, which are
// compiled as if from the source package of the generic function.
var pkg *types.Pkg
if len(clo.Func.ClosureVars) == 0 {
pkg = types.LocalPkg
} else {
for _, v := range clo.Func.ClosureVars {
if pkg == nil {
pkg = v.Sym().Pkg
} else if pkg != v.Sym().Pkg {
base.Fatalf("Closure variables from multiple packages")
}
}
}
fields := []*types.Field{
types.NewField(base.Pos, pkg.Lookup(".F"), types.Types[types.TUINTPTR]),
}
for _, v := range clo.Func.ClosureVars {
typ := v.Type()
if !v.Byval() {
typ = types.NewPtr(typ)
}
fields = append(fields, types.NewField(base.Pos, v.Sym(), typ))
}
typ := types.NewStruct(types.NoPkg, fields)
typ.SetNoalg(true)
return typ
}
// MethodValueType returns the struct type used to hold all the information
// needed in the closure for a OMETHVALUE node. The address of a variable of
// the returned type can be cast to a func.
func MethodValueType(n *ir.SelectorExpr) *types.Type {
t := types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(base.Pos, Lookup("F"), types.Types[types.TUINTPTR]),
types.NewField(base.Pos, Lookup("R"), n.X.Type()),
})
t.SetNoalg(true)
return t
}
// True if we are typechecking an inline body in ImportedBody below. We use this
// flag to not create a new closure function in tcClosure when we are just
// typechecking an inline body, as opposed to the body of a real function.
var inTypeCheckInl bool
// ImportedBody returns immediately if the inlining information for fn is
// populated. Otherwise, fn must be an imported function. If so, ImportedBody
// loads in the dcls and body for fn, and typechecks as needed.
func ImportedBody(fn *ir.Func) {
if fn.Inl.Body != nil {
return
}
lno := ir.SetPos(fn.Nname)
// When we load an inlined body, we need to allow OADDR
// operations on untyped expressions. We will fix the
// addrtaken flags on all the arguments of the OADDR with the
// computeAddrtaken call below (after we typecheck the body).
// TODO: export/import types and addrtaken marks along with inlined bodies,
// so this will be unnecessary.
IncrementalAddrtaken = false
defer func() {
if DirtyAddrtaken {
ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available
DirtyAddrtaken = false
}
IncrementalAddrtaken = true
}()
ImportBody(fn)
// Stmts(fn.Inl.Body) below is only for imported functions;
// their bodies may refer to unsafe as long as the package
// was marked safe during import (which was checked then).
// the ->inl of a local function has been typechecked before CanInline copied it.
pkg := fnpkg(fn.Nname)
if pkg == types.LocalPkg || pkg == nil {
return // ImportedBody on local function
}
if base.Flag.LowerM > 2 || base.Debug.Export != 0 {
fmt.Printf("typecheck import [%v] %L { %v }\n", fn.Sym(), fn, ir.Nodes(fn.Inl.Body))
}
if !go117ExportTypes {
// If we didn't export & import types, typecheck the code here.
savefn := ir.CurFunc
ir.CurFunc = fn
if inTypeCheckInl {
base.Fatalf("inTypeCheckInl should not be set recursively")
}
inTypeCheckInl = true
Stmts(fn.Inl.Body)
inTypeCheckInl = false
ir.CurFunc = savefn
}
base.Pos = lno
}
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
// the ->sym can be re-used in the local package, so peel it off the receiver's type.
func fnpkg(fn *ir.Name) *types.Pkg {
if ir.IsMethod(fn) {
// method
rcvr := fn.Type().Recv().Type
if rcvr.IsPtr() {
rcvr = rcvr.Elem()
}
if rcvr.Sym() == nil {
base.Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym(), fn, rcvr)
}
return rcvr.Sym().Pkg
}
// non-method
return fn.Sym().Pkg
}
// tcClosure typechecks an OCLOSURE node. It also creates the named
// function associated with the closure.
// TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking.
func tcClosure(clo *ir.ClosureExpr, top int) ir.Node {
fn := clo.Func
// We used to allow IR builders to typecheck the underlying Func
// themselves, but that led to too much variety and inconsistency
// around who's responsible for naming the function, typechecking
// it, or adding it to Target.Decls.
//
// It's now all or nothing. Callers are still allowed to do these
// themselves, but then they assume responsibility for all of them.
if fn.Typecheck() == 1 {
base.FatalfAt(fn.Pos(), "underlying closure func already typechecked: %v", fn)
}
// Set current associated iota value, so iota can be used inside
// function in ConstSpec, see issue #22344
if x := getIotaValue(); x >= 0 {
fn.Iota = x
}
ir.NameClosure(clo, ir.CurFunc)
Func(fn)
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
// ready to type check code yet; we'll check it later, because the
// underlying closure function we create is added to Target.Decls.
if ir.CurFunc != nil {
oldfn := ir.CurFunc
ir.CurFunc = fn
Stmts(fn.Body)
ir.CurFunc = oldfn
}
out := 0
for _, v := range fn.ClosureVars {
if v.Type() == nil {
// If v.Type is nil, it means v looked like it was going to be
// used in the closure, but isn't. This happens in struct
// literals like s{f: x} where we can't distinguish whether f is
// a field identifier or expression until resolving s.
continue
}
// type check closed variables outside the closure, so that the
// outer frame also captures them.
Expr(v.Outer)
fn.ClosureVars[out] = v
out++
}
fn.ClosureVars = fn.ClosureVars[:out]
clo.SetType(fn.Type())
target := Target
if inTypeCheckInl {
// We're typechecking an imported function, so it's not actually
// part of Target. Skip adding it to Target.Decls so we don't
// compile it again.
target = nil
}
return ir.UseClosure(clo, target)
}
// type check function definition
// To be called by typecheck, not directly.
// (Call typecheck.Func instead.)
func tcFunc(n *ir.Func) {
if base.EnableTrace && base.Flag.LowerT {
defer tracePrint("tcFunc", n)(nil)
}
n.Nname = AssignExpr(n.Nname).(*ir.Name)
t := n.Nname.Type()
if t == nil {
return
}
rcvr := t.Recv()
if rcvr != nil && n.Shortname != nil {
m := addmethod(n, n.Shortname, t, true, n.Pragma&ir.Nointerface != 0)
if m == nil {
return
}
n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname))
Declare(n.Nname, ir.PFUNC)
}
}
// tcCall typechecks an OCALL node.
func tcCall(n *ir.CallExpr, top int) ir.Node {
Stmts(n.Init()) // imported rewritten f(g()) calls (#30907)
n.X = typecheck(n.X, ctxExpr|ctxType|ctxCallee)
if n.X.Diag() {
n.SetDiag(true)
}
l := n.X
if l.Op() == ir.ONAME && l.(*ir.Name).BuiltinOp != 0 {
l := l.(*ir.Name)
if n.IsDDD && l.BuiltinOp != ir.OAPPEND {
base.Errorf("invalid use of ... with builtin %v", l)
}
// builtin: OLEN, OCAP, etc.
switch l.BuiltinOp {
default:
base.Fatalf("unknown builtin %v", l)
case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
n.SetOp(l.BuiltinOp)
n.X = nil
n.SetTypecheck(0) // re-typechecking new op is OK, not a loop
return typecheck(n, top)
case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL:
typecheckargs(n)
fallthrough
case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
arg, ok := needOneArg(n, "%v", n.Op())
if !ok {
n.SetType(nil)
return n
}
u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg)
return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init
case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
typecheckargs(n)
arg1, arg2, ok := needTwoArgs(n)
if !ok {
n.SetType(nil)
return n
}
b := ir.NewBinaryExpr(n.Pos(), l.BuiltinOp, arg1, arg2)
return typecheck(ir.InitExpr(n.Init(), b), top) // typecheckargs can add to old.Init
}
panic("unreachable")
}
n.X = DefaultLit(n.X, nil)
l = n.X
if l.Op() == ir.OTYPE {
if n.IsDDD {
if !l.Type().Broke() {
base.Errorf("invalid use of ... in type conversion to %v", l.Type())
}
n.SetDiag(true)
}
// pick off before type-checking arguments
arg, ok := needOneArg(n, "conversion to %v", l.Type())
if !ok {
n.SetType(nil)
return n
}
n := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
n.SetType(l.Type())
return tcConv(n)
}
typecheckargs(n)
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
types.CheckSize(t)
switch l.Op() {
case ir.ODOTINTER:
n.SetOp(ir.OCALLINTER)
case ir.ODOTMETH:
l := l.(*ir.SelectorExpr)
n.SetOp(ir.OCALLMETH)
// typecheckaste was used here but there wasn't enough
// information further down the call chain to know if we
// were testing a method receiver for unexported fields.
// It isn't necessary, so just do a sanity check.
tp := t.Recv().Type
if l.X == nil || !types.Identical(l.X.Type(), tp) {
base.Fatalf("method receiver")
}
default:
n.SetOp(ir.OCALLFUNC)
if t.Kind() != types.TFUNC {
if o := ir.Orig(l); o.Name() != nil && types.BuiltinPkg.Lookup(o.Sym().Name).Def != nil {
// be more specific when the non-function
// name matches a predeclared function
base.Errorf("cannot call non-function %L, declared at %s",
l, base.FmtPos(o.Name().Pos()))
} else {
base.Errorf("cannot call non-function %L", l)
}
n.SetType(nil)
return n
}
}
typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) })
FixMethodCall(n)
if t.NumResults() == 0 {
return n
}
if t.NumResults() == 1 {
n.SetType(l.Type().Results().Field(0).Type)
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
// Emit code for runtime.getg() directly instead of calling function.
// Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
// so that the ordering pass can make sure to preserve the semantics of the original code
// (in particular, the exact time of the function call) by introducing temporaries.
// In this case, we know getg() always returns the same result within a given function
// and we want to avoid the temporaries, so we do the rewrite earlier than is typical.
n.SetOp(ir.OGETG)
}
}
return n
}
// multiple return
if top&(ctxMultiOK|ctxStmt) == 0 {
base.Errorf("multiple-value %v() in single-value context", l)
return n
}
n.SetType(l.Type().Results())
return n
}
// tcAppend typechecks an OAPPEND node.
func tcAppend(n *ir.CallExpr) ir.Node {
typecheckargs(n)
args := n.Args
if len(args) == 0 {
base.Errorf("missing arguments to append")
n.SetType(nil)
return n
}
t := args[0].Type()
if t == nil {
n.SetType(nil)
return n
}
n.SetType(t)
if !t.IsSlice() {
if ir.IsNil(args[0]) {
base.Errorf("first argument to append must be typed slice; have untyped nil")
n.SetType(nil)
return n
}
base.Errorf("first argument to append must be slice; have %L", t)
n.SetType(nil)
return n
}
if n.IsDDD {
if len(args) == 1 {
base.Errorf("cannot use ... on first argument to append")
n.SetType(nil)
return n
}
if len(args) != 2 {
base.Errorf("too many arguments to append")
n.SetType(nil)
return n
}
if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() {
args[1] = DefaultLit(args[1], types.Types[types.TSTRING])
return n
}
args[1] = AssignConv(args[1], t.Underlying(), "append")
return n
}
as := args[1:]
for i, n := range as {
if n.Type() == nil {
continue
}
as[i] = AssignConv(n, t.Elem(), "append")
types.CheckSize(as[i].Type()) // ensure width is calculated for backend
}
return n
}
// tcClose typechecks an OCLOSE node.
func tcClose(n *ir.UnaryExpr) ir.Node {
n.X = Expr(n.X)
n.X = DefaultLit(n.X, nil)
l := n.X
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
if !t.IsChan() {
base.Errorf("invalid operation: %v (non-chan type %v)", n, t)
n.SetType(nil)
return n
}
if !t.ChanDir().CanSend() {
base.Errorf("invalid operation: %v (cannot close receive-only channel)", n)
n.SetType(nil)
return n
}
return n
}
// tcComplex typechecks an OCOMPLEX node.
func tcComplex(n *ir.BinaryExpr) ir.Node {
l := Expr(n.X)
r := Expr(n.Y)
if l.Type() == nil || r.Type() == nil {
n.SetType(nil)
return n
}
l, r = defaultlit2(l, r, false)
if l.Type() == nil || r.Type() == nil {
n.SetType(nil)
return n
}
n.X = l
n.Y = r
if !types.Identical(l.Type(), r.Type()) {
base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type())
n.SetType(nil)
return n
}
var t *types.Type
switch l.Type().Kind() {
default:
base.Errorf("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type())
n.SetType(nil)
return n
case types.TIDEAL:
t = types.UntypedComplex
case types.TFLOAT32:
t = types.Types[types.TCOMPLEX64]
case types.TFLOAT64:
t = types.Types[types.TCOMPLEX128]
}
n.SetType(t)
return n
}
// tcCopy typechecks an OCOPY node.
func tcCopy(n *ir.BinaryExpr) ir.Node {
n.SetType(types.Types[types.TINT])
n.X = Expr(n.X)
n.X = DefaultLit(n.X, nil)
n.Y = Expr(n.Y)
n.Y = DefaultLit(n.Y, nil)
if n.X.Type() == nil || n.Y.Type() == nil {
n.SetType(nil)
return n
}
// copy([]byte, string)
if n.X.Type().IsSlice() && n.Y.Type().IsString() {
if types.Identical(n.X.Type().Elem(), types.ByteType) {
return n
}
base.Errorf("arguments to copy have different element types: %L and string", n.X.Type())
n.SetType(nil)
return n
}
if !n.X.Type().IsSlice() || !n.Y.Type().IsSlice() {
if !n.X.Type().IsSlice() && !n.Y.Type().IsSlice() {
base.Errorf("arguments to copy must be slices; have %L, %L", n.X.Type(), n.Y.Type())
} else if !n.X.Type().IsSlice() {
base.Errorf("first argument to copy should be slice; have %L", n.X.Type())
} else {
base.Errorf("second argument to copy should be slice or string; have %L", n.Y.Type())
}
n.SetType(nil)
return n
}
if !types.Identical(n.X.Type().Elem(), n.Y.Type().Elem()) {
base.Errorf("arguments to copy have different element types: %L and %L", n.X.Type(), n.Y.Type())
n.SetType(nil)
return n
}
return n
}
// tcDelete typechecks an ODELETE node.
func tcDelete(n *ir.CallExpr) ir.Node {
typecheckargs(n)
args := n.Args
if len(args) == 0 {
base.Errorf("missing arguments to delete")
n.SetType(nil)
return n
}
if len(args) == 1 {
base.Errorf("missing second (key) argument to delete")
n.SetType(nil)
return n
}
if len(args) != 2 {
base.Errorf("too many arguments to delete")
n.SetType(nil)
return n
}
l := args[0]
r := args[1]
if l.Type() != nil && !l.Type().IsMap() {
base.Errorf("first argument to delete must be map; have %L", l.Type())
n.SetType(nil)
return n
}
args[1] = AssignConv(r, l.Type().Key(), "delete")
return n
}
// tcMake typechecks an OMAKE node.
func tcMake(n *ir.CallExpr) ir.Node {
args := n.Args
if len(args) == 0 {
base.Errorf("missing argument to make")
n.SetType(nil)
return n
}
n.Args = nil
l := args[0]
l = typecheck(l, ctxType)
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
i := 1
var nn ir.Node
switch t.Kind() {
default:
base.Errorf("cannot make type %v", t)
n.SetType(nil)
return n
case types.TSLICE:
if i >= len(args) {
base.Errorf("missing len argument to make(%v)", t)
n.SetType(nil)
return n
}
l = args[i]
i++
l = Expr(l)
var r ir.Node
if i < len(args) {
r = args[i]
i++
r = Expr(r)
}
if l.Type() == nil || (r != nil && r.Type() == nil) {
n.SetType(nil)
return n
}
if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) {
n.SetType(nil)
return n
}
if ir.IsConst(l, constant.Int) && r != nil && ir.IsConst(r, constant.Int) && constant.Compare(l.Val(), token.GTR, r.Val()) {
base.Errorf("len larger than cap in make(%v)", t)
n.SetType(nil)
return n
}
nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r)
case types.TMAP:
if i < len(args) {
l = args[i]
i++
l = Expr(l)
l = DefaultLit(l, types.Types[types.TINT])
if l.Type() == nil {
n.SetType(nil)
return n
}
if !checkmake(t, "size", &l) {
n.SetType(nil)
return n
}
} else {
l = ir.NewInt(0)
}
nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil)
nn.SetEsc(n.Esc())
case types.TCHAN:
l = nil
if i < len(args) {
l = args[i]
i++
l = Expr(l)
l = DefaultLit(l, types.Types[types.TINT])
if l.Type() == nil {
n.SetType(nil)
return n
}
if !checkmake(t, "buffer", &l) {
n.SetType(nil)
return n
}
} else {
l = ir.NewInt(0)
}
nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil)
}
if i < len(args) {
base.Errorf("too many arguments to make(%v)", t)
n.SetType(nil)
return n
}
nn.SetType(t)
return nn
}
// tcMakeSliceCopy typechecks an OMAKESLICECOPY node.
func tcMakeSliceCopy(n *ir.MakeExpr) ir.Node {
// Errors here are Fatalf instead of Errorf because only the compiler
// can construct an OMAKESLICECOPY node.
// Components used in OMAKESCLICECOPY that are supplied by parsed source code
// have already been typechecked in OMAKE and OCOPY earlier.
t := n.Type()
if t == nil {
base.Fatalf("no type specified for OMAKESLICECOPY")
}
if !t.IsSlice() {
base.Fatalf("invalid type %v for OMAKESLICECOPY", n.Type())
}
if n.Len == nil {
base.Fatalf("missing len argument for OMAKESLICECOPY")
}
if n.Cap == nil {
base.Fatalf("missing slice argument to copy for OMAKESLICECOPY")
}
n.Len = Expr(n.Len)
n.Cap = Expr(n.Cap)
n.Len = DefaultLit(n.Len, types.Types[types.TINT])
if !n.Len.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
base.Errorf("non-integer len argument in OMAKESLICECOPY")
}
if ir.IsConst(n.Len, constant.Int) {
if ir.ConstOverflow(n.Len.Val(), types.Types[types.TINT]) {
base.Fatalf("len for OMAKESLICECOPY too large")
}
if constant.Sign(n.Len.Val()) < 0 {
base.Fatalf("len for OMAKESLICECOPY must be non-negative")
}
}
return n
}
// tcNew typechecks an ONEW node.
func tcNew(n *ir.UnaryExpr) ir.Node {
if n.X == nil {
// Fatalf because the OCALL above checked for us,
// so this must be an internally-generated mistake.
base.Fatalf("missing argument to new")
}
l := n.X
l = typecheck(l, ctxType)
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
n.X = l
n.SetType(types.NewPtr(t))
return n
}
// tcPanic typechecks an OPANIC node.
func tcPanic(n *ir.UnaryExpr) ir.Node {
n.X = Expr(n.X)
n.X = AssignConv(n.X, types.Types[types.TINTER], "argument to panic")
if n.X.Type() == nil {
n.SetType(nil)
return n
}
return n
}
// tcPrint typechecks an OPRINT or OPRINTN node.
func tcPrint(n *ir.CallExpr) ir.Node {
typecheckargs(n)
ls := n.Args
for i1, n1 := range ls {
// Special case for print: int constant is int64, not int.
if ir.IsConst(n1, constant.Int) {
ls[i1] = DefaultLit(ls[i1], types.Types[types.TINT64])
} else {
ls[i1] = DefaultLit(ls[i1], nil)
}
}
return n
}
// tcRealImag typechecks an OREAL or OIMAG node.
func tcRealImag(n *ir.UnaryExpr) ir.Node {
n.X = Expr(n.X)
l := n.X
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
// Determine result type.
switch t.Kind() {
case types.TIDEAL:
n.SetType(types.UntypedFloat)
case types.TCOMPLEX64:
n.SetType(types.Types[types.TFLOAT32])
case types.TCOMPLEX128:
n.SetType(types.Types[types.TFLOAT64])
default:
base.Errorf("invalid argument %L for %v", l, n.Op())
n.SetType(nil)
return n
}
return n
}
// tcRecover typechecks an ORECOVER node.
func tcRecover(n *ir.CallExpr) ir.Node {
if len(n.Args) != 0 {
base.Errorf("too many arguments to recover")
n.SetType(nil)
return n
}
n.SetType(types.Types[types.TINTER])
return n
}
// tcRecoverFP typechecks an ORECOVERFP node.
func tcRecoverFP(n *ir.CallExpr) ir.Node {
if len(n.Args) != 1 {
base.FatalfAt(n.Pos(), "wrong number of arguments: %v", n)
}
n.Args[0] = Expr(n.Args[0])
if !n.Args[0].Type().IsPtrShaped() {
base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.Args[0])
}
n.SetType(types.Types[types.TINTER])
return n
}
// tcUnsafeAdd typechecks an OUNSAFEADD node.
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Add")
n.SetType(nil)
return n
}
n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add")
n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT])
if n.X.Type() == nil || n.Y.Type() == nil {
n.SetType(nil)
return n
}
if !n.Y.Type().IsInteger() {
n.SetType(nil)
return n
}
n.SetType(n.X.Type())
return n
}
// tcUnsafeSlice typechecks an OUNSAFESLICE node.
func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Slice")
n.SetType(nil)
return n
}
n.X = Expr(n.X)
n.Y = Expr(n.Y)
if n.X.Type() == nil || n.Y.Type() == nil {
n.SetType(nil)
return n
}
t := n.X.Type()
if !t.IsPtr() {
base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t)
} else if t.Elem().NotInHeap() {
// TODO(mdempsky): This can be relaxed, but should only affect the
// Go runtime itself. End users should only see //go:notinheap
// types due to incomplete C structs in cgo, and those types don't
// have a meaningful size anyway.
base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed")
}
if !checkunsafeslice(&n.Y) {
n.SetType(nil)
return n
}
n.SetType(types.NewSlice(t.Elem()))
return n
}