aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck/func.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-12-23 00:43:42 -0500
committerRuss Cox <rsc@golang.org>2020-12-23 06:38:34 +0000
commit0256ba99a893f2faf870105fc93fff94e5caf241 (patch)
treec5bf15e6d6d6d68db1f3f7b01cb4d5fd696216e0 /src/cmd/compile/internal/typecheck/func.go
parentb9693d7627089204e6c2448f543c3512d86dae70 (diff)
downloadgo-0256ba99a893f2faf870105fc93fff94e5caf241.tar.gz
go-0256ba99a893f2faf870105fc93fff94e5caf241.zip
[dev.regabi] cmd/compile: split up typecheck1 [generated]
typecheck1 is the largest non-machine-generated function in the compiler. weighing in at 1,747 lines. Since we are destroying the git blame history anyway, now is a good time to split each different case into its own function, making future work on this function more manageable. [git-generate] cd src/cmd/compile/internal/typecheck rf ' # Remove tracing print from typecheck1 - the one in typecheck is fine. # Removing it lets us remove the named result. # That lets all the cut-out functions not have named results. rm typecheck.go:/^func typecheck1/+0,/^func typecheck1/+4 sub typecheck.go:/^func typecheck1/+/\(res ir\.Node\)/ ir.Node mv typecheckselect tcSelect mv typecheckswitch tcSwitch mv typecheckrange tcRange mv typecheckfunc tcFunc mv checkdefergo tcGoDefer mv typecheckclosure tcClosure mv check typecheck mv typecheckcomplit tcCompLit mv typecheckas tcAssign mv typecheckas2 tcAssignList mv typecheckpartialcall tcCallPart mv typecheckExprSwitch tcSwitchExpr mv typecheckTypeSwitch tcSwitchType mv typecheck1:/^\tcase ir.ORETURN:/+2,/^\tcase /-2 tcReturn add typecheck.go:/^func tcReturn/-0 \ // tcReturn typechecks an ORETURN node. mv typecheck1:/^\tcase ir.OIF:/+2,/^\tcase /-2 tcIf add typecheck.go:/^func tcIf/-0 \ // tcIf typechecks an OIF node. mv typecheck1:/^\tcase ir.OFOR,/+2,/^\tcase /-2 tcFor add typecheck.go:/^func tcFor/-0 \ // tcFor typechecks an OFOR node. mv typecheck1:/^\tcase ir.OSPTR:/+2,/^\tcase /-2 tcSPtr add typecheck.go:/^func tcSPtr/-0 \ // tcSPtr typechecks an OSPTR node. mv typecheck1:/^\tcase ir.OITAB:/+2,/^\tcase /-2 tcITab add typecheck.go:/^func tcITab/-0 \ // tcITab typechecks an OITAB node. mv typecheck1:/^\tcase ir.ORECOVER:/+2,/^\tcase /-2 tcRecover add typecheck.go:/^func tcRecover/-0 \ // tcRecover typechecks an ORECOVER node. mv typecheck1:/^\tcase ir.OPANIC:/+2,/^\tcase /-2 tcPanic add typecheck.go:/^func tcPanic/-0 \ // tcPanic typechecks an OPANIC node. mv typecheck1:/^\tcase ir.OPRINT,/+2,/^\tcase /-2 tcPrint add typecheck.go:/^func tcPrint/-0 \ // tcPrint typechecks an OPRINT or OPRINTN node. mv typecheck1:/^\tcase ir.ONEW:/+2,/^\tcase /-2 tcNew add typecheck.go:/^func tcNew/-0 \ // tcNew typechecks an ONEW node. mv typecheck1:/^\tcase ir.OMAKE:/+2,/^\tcase /-2 tcMake add typecheck.go:/^func tcMake/-0 \ // tcMake typechecks an OMAKE node. mv typecheck1:/^\tcase ir.OCONV:/+2,/^\tcase /-2 tcConv add typecheck.go:/^func tcConv/-0 \ // tcConv typechecks an OCONV node. mv typecheck1:/^\tcase ir.OCOPY:/+2,/^\tcase /-2 tcCopy add typecheck.go:/^func tcCopy/-0 \ // tcCopy typechecks an OCOPY node. mv typecheck1:/^\tcase ir.OAPPEND:/+2,/^\tcase /-2 tcAppend add typecheck.go:/^func tcAppend/-0 \ // tcAppend typechecks an OAPPEND node. mv typecheck1:/^\tcase ir.ODELETE:/+2,/^\tcase /-2 tcDelete add typecheck.go:/^func tcDelete/-0 \ // tcDelete typechecks an ODELETE node. mv typecheck1:/^\tcase ir.OCLOSE:/+2,/^\tcase /-2 tcClose add typecheck.go:/^func tcClose/-0 \ // tcClose typechecks an OCLOSE node. mv typecheck1:/^\tcase ir.OCOMPLEX:/+2,/^\tcase /-2 tcComplex add typecheck.go:/^func tcComplex/-0 \ // tcComplex typechecks an OCOMPLEX node. mv typecheck1:/^\tcase ir.OREAL,/+2,/^\tcase /-2 tcRealImag add typecheck.go:/^func tcRealImag/-0 \ // tcRealImag typechecks an OREAL or OIMAG node. mv typecheck1:/^\tcase ir.OCAP,/+2,/^\tcase /-2 tcLenCap add typecheck.go:/^func tcLenCap/-0 \ // tcLenCap typechecks an OLEN or OCAP node. mv typecheck1:/^\tcase ir.OCALL:/+2,/^\tcase /-2 tcCall add typecheck.go:/^func tcCall/-0 \ // tcCall typechecks an OCALL node. mv typecheck1:/^\tcase ir.OSLICE,/+2,/^\tcase /-3 tcSlice add typecheck.go:/^func tcSlice/-0 \ // tcSlice typechecks an OSLICE or OSLICE3 node. # move type assertion above comment mv typecheck1:/^\tcase ir.OMAKESLICECOPY:/+/n := n/-+ typecheck1:/^\tcase ir.OMAKESLICECOPY:/+0 mv typecheck1:/^\tcase ir.OMAKESLICECOPY:/+2,/^\tcase /-2 tcMakeSliceCopy add typecheck.go:/^func tcMakeSliceCopy/-0 \ // tcMakeSliceCopy typechecks an OMAKESLICECOPY node. # move type assertion above comment mv typecheck1:/^\tcase ir.OSLICEHEADER:/+/n := n/-+ typecheck1:/^\tcase ir.OSLICEHEADER:/+0 mv typecheck1:/^\tcase ir.OSLICEHEADER:/+2,/^\tcase /-2 tcSliceHeader add typecheck.go:/^func tcSliceHeader/-0 \ // tcSliceHeader typechecks an OSLICEHEADER node. mv typecheck1:/^\tcase ir.OSEND:/+2,/^\tcase /-2 tcSend add typecheck.go:/^func tcSend/-0 \ // tcSend typechecks an OSEND node. mv typecheck1:/^\tcase ir.ORECV:/+2,/^\tcase /-2 tcRecv add typecheck.go:/^func tcRecv/-0 \ // tcRecv typechecks an ORECV node. mv typecheck1:/^\tcase ir.OINDEX:/+2,/^\tcase /-2 tcIndex add typecheck.go:/^func tcIndex/-0 \ // tcIndex typechecks an OINDEX node. mv typecheck1:/^\tcase ir.ODOTTYPE:/+2,/^\tcase /-2 tcDotType add typecheck.go:/^func tcDotType/-0 \ // tcDotType typechecks an ODOTTYPE node. mv typecheck1:/^\tcase ir.OXDOT,/+2,/^\tcase /-2 tcDot add typecheck.go:/^func tcDot/-0 \ // tcDot typechecks an OXDOT or ODOT node. mv typecheck1:/^\tcase ir.OADDR:/+2,/^\tcase /-2 tcAddr add typecheck.go:/^func tcAddr/-0 \ // tcAddr typechecks an OADDR node. mv typecheck1:/^\tcase ir.OBITNOT,/+2,/^\tcase /-3 tcUnaryArith add typecheck.go:/^func tcUnaryArith/-0 \ // tcUnaryArith typechecks a unary arithmetic expression. mv typecheck1:/^\t\tir.OXOR:/+1,/^\tcase /-2 tcArith add typecheck.go:/^func tcArith/-0 \ // tcArith typechecks a binary arithmetic expression. mv typecheck1:/^\tcase ir.ODEREF:/+2,/^\tcase /-2 tcStar add typecheck.go:/^func tcStar/-0 \ // tcStar typechecks an ODEREF node, which may be an expression or a type. mv typecheck1:/^\tcase ir.OTFUNC:/+2,/^\tcase /-2 tcFuncType add typecheck.go:/^func tcFuncType/-0 \ // tcFuncType typechecks an OTFUNC node. mv typecheck1:/^\tcase ir.OTINTER:/+2,/^\tcase /-2 tcInterfaceType add typecheck.go:/^func tcInterfaceType/-0 \ // tcInterfaceType typechecks an OTINTER node. mv typecheck1:/^\tcase ir.OTSTRUCT:/+2,/^\tcase /-2 tcStructType add typecheck.go:/^func tcStructType/-0 \ // tcStructType typechecks an OTSTRUCT node. mv typecheck1:/^\tcase ir.OTCHAN:/+2,/^\tcase /-2 tcChanType add typecheck.go:/^func tcChanType/-0 \ // tcChanType typechecks an OTCHAN node. mv typecheck1:/^\tcase ir.OTMAP:/+2,/^\tcase /-2 tcMapType add typecheck.go:/^func tcMapType/-0 \ // tcMapType typechecks an OTMAP node. mv typecheck1:/^\tcase ir.OTARRAY:/+2,/^\tcase /-2 tcArrayType add typecheck.go:/^func tcArrayType/-0 \ // tcArrayType typechecks an OTARRAY node. mv typecheck1:/^\tcase ir.OTSLICE:/+2,/^\tcase /-2 tcSliceType add typecheck.go:/^func tcSliceType/-0 \ // tcSliceType typechecks an OTSLICE node. mv \ tcAssign \ tcAssignList \ tcFor \ tcGoDefer \ tcIf \ tcRange \ tcReturn \ tcSelect \ tcSend \ tcSwitch \ tcSwitchExpr \ tcSwitchType \ typeSet \ typeSetEntry \ typeSet.add \ stmt1.go mv stmt1.go stmt.go mv \ tcAddr \ tcArith \ tcArrayType \ tcChanType \ tcClosure \ tcCompLit \ tcConv \ tcDot \ tcDotType \ tcFuncType \ tcITab \ tcIndex \ tcInterfaceType \ tcLenCap \ tcMapType \ tcRecv \ tcSPtr \ tcSlice \ tcSliceHeader \ tcSliceType \ tcStar \ tcStructType \ tcUnaryArith \ expr.go mv \ tcClosure \ tcCallPart \ tcFunc \ tcCall \ tcAppend \ tcClose \ tcComplex \ tcCopy \ tcDelete \ tcMake \ tcMakeSliceCopy \ tcNew \ tcPanic \ tcPrint \ tcRealImag \ tcRecover \ func1.go mv func1.go func.go mv \ tcArrayType \ tcChanType \ tcFuncType \ tcInterfaceType \ tcMapType \ tcSliceType \ tcStructType \ type.go ' Change-Id: I0fb0a3039005bc1783575291daff1e6c306895ff Reviewed-on: https://go-review.googlesource.com/c/go/+/279429 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile/internal/typecheck/func.go')
-rw-r--r--src/cmd/compile/internal/typecheck/func.go753
1 files changed, 692 insertions, 61 deletions
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index 4675de6cad..99d81dcede 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -10,6 +10,8 @@ import (
"cmd/compile/internal/types"
"fmt"
+ "go/constant"
+ "go/token"
)
// package all the arguments that match a ... T parameter into a []T.
@@ -156,66 +158,6 @@ func CaptureVars(fn *ir.Func) {
base.Pos = lno
}
-// typecheckclosure 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 typecheckclosure(clo *ir.ClosureExpr, top int) {
- fn := clo.Func
- // 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
- }
-
- fn.ClosureType = check(fn.ClosureType, ctxType)
- clo.SetType(fn.ClosureType.Type())
- fn.SetClosureCalled(top&ctxCallee != 0)
-
- // Do not typecheck fn twice, otherwise, we will end up pushing
- // fn to Target.Decls multiple times, causing initLSym called twice.
- // See #30709
- if fn.Typecheck() == 1 {
- return
- }
-
- for _, ln := range fn.ClosureVars {
- n := ln.Defn
- if !n.Name().Captured() {
- n.Name().SetCaptured(true)
- if n.Name().Decldepth == 0 {
- base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n)
- }
-
- // Ignore assignments to the variable in straightline code
- // preceding the first capturing by a closure.
- if n.Name().Decldepth == decldepth {
- n.Name().SetAssigned(false)
- }
- }
- }
-
- fn.Nname.SetSym(closurename(ir.CurFunc))
- ir.MarkFunc(fn.Nname)
- 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 && clo.Type() != nil {
- oldfn := ir.CurFunc
- ir.CurFunc = fn
- olddd := decldepth
- decldepth = 1
- Stmts(fn.Body)
- decldepth = olddd
- ir.CurFunc = oldfn
- }
-
- Target.Decls = append(Target.Decls, fn)
-}
-
// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
// because they're a copy of an already checked body.
func ImportedBody(fn *ir.Func) {
@@ -380,7 +322,67 @@ func makepartialcall(dot *ir.SelectorExpr, t0 *types.Type, meth *types.Sym) *ir.
return fn
}
-func typecheckpartialcall(n ir.Node, sym *types.Sym) *ir.CallPartExpr {
+// 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) {
+ fn := clo.Func
+ // 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
+ }
+
+ fn.ClosureType = typecheck(fn.ClosureType, ctxType)
+ clo.SetType(fn.ClosureType.Type())
+ fn.SetClosureCalled(top&ctxCallee != 0)
+
+ // Do not typecheck fn twice, otherwise, we will end up pushing
+ // fn to Target.Decls multiple times, causing initLSym called twice.
+ // See #30709
+ if fn.Typecheck() == 1 {
+ return
+ }
+
+ for _, ln := range fn.ClosureVars {
+ n := ln.Defn
+ if !n.Name().Captured() {
+ n.Name().SetCaptured(true)
+ if n.Name().Decldepth == 0 {
+ base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n)
+ }
+
+ // Ignore assignments to the variable in straightline code
+ // preceding the first capturing by a closure.
+ if n.Name().Decldepth == decldepth {
+ n.Name().SetAssigned(false)
+ }
+ }
+ }
+
+ fn.Nname.SetSym(closurename(ir.CurFunc))
+ ir.MarkFunc(fn.Nname)
+ 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 && clo.Type() != nil {
+ oldfn := ir.CurFunc
+ ir.CurFunc = fn
+ olddd := decldepth
+ decldepth = 1
+ Stmts(fn.Body)
+ decldepth = olddd
+ ir.CurFunc = oldfn
+ }
+
+ Target.Decls = append(Target.Decls, fn)
+}
+
+func tcCallPart(n ir.Node, sym *types.Sym) *ir.CallPartExpr {
switch n.Op() {
case ir.ODOTINTER, ir.ODOTMETH:
break
@@ -396,3 +398,632 @@ func typecheckpartialcall(n ir.Node, sym *types.Sym) *ir.CallPartExpr {
return ir.NewCallPartExpr(dot.Pos(), dot.X, dot.Selection, fn)
}
+
+// type check function definition
+// To be called by typecheck, not directly.
+// (Call typecheckFunc instead.)
+func tcFunc(n *ir.Func) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheckfunc", n)(nil)
+ }
+
+ for _, ln := range n.Dcl {
+ if ln.Op() == ir.ONAME && (ln.Class_ == ir.PPARAM || ln.Class_ == ir.PPARAMOUT) {
+ ln.Decldepth = 1
+ }
+ }
+
+ n.Nname = AssignExpr(n.Nname).(*ir.Name)
+ t := n.Nname.Type()
+ if t == nil {
+ return
+ }
+ n.SetType(t)
+ 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)
+ }
+
+ if base.Ctxt.Flag_dynlink && !inimport && n.Nname != nil {
+ NeedFuncSym(n.Sym())
+ }
+}
+
+// tcCall typechecks an OCALL node.
+func tcCall(n *ir.CallExpr, top int) ir.Node {
+ n.Use = ir.CallUseExpr
+ if top == ctxStmt {
+ n.Use = ir.CallUseStmt
+ }
+ 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:
+ 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 typecheck1(n, top)
+ }
+
+ 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 {
+ // TODO(mdempsky): Remove "o.Sym() != nil" once we stop
+ // using ir.Name for numeric literals.
+ if o := ir.Orig(l); o.Name() != nil && o.Sym() != 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) })
+ 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.Set(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 = DefaultLit(n.X, types.Types[types.TINTER])
+ 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
+}