diff options
author | Russ Cox <rsc@golang.org> | 2020-12-23 00:43:42 -0500 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2020-12-23 06:38:34 +0000 |
commit | 0256ba99a893f2faf870105fc93fff94e5caf241 (patch) | |
tree | c5bf15e6d6d6d68db1f3f7b01cb4d5fd696216e0 /src/cmd/compile/internal/typecheck/func.go | |
parent | b9693d7627089204e6c2448f543c3512d86dae70 (diff) | |
download | go-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.go | 753 |
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 +} |