aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/typecheck/stmt.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/stmt.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/stmt.go')
-rw-r--r--src/cmd/compile/internal/typecheck/stmt.go433
1 files changed, 375 insertions, 58 deletions
diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go
index 889ee06d6e..bf3801eea2 100644
--- a/src/cmd/compile/internal/typecheck/stmt.go
+++ b/src/cmd/compile/internal/typecheck/stmt.go
@@ -11,33 +11,6 @@ import (
"cmd/internal/src"
)
-// range
-func typecheckrange(n *ir.RangeStmt) {
- // Typechecking order is important here:
- // 0. first typecheck range expression (slice/map/chan),
- // it is evaluated only once and so logically it is not part of the loop.
- // 1. typecheck produced values,
- // this part can declare new vars and so it must be typechecked before body,
- // because body can contain a closure that captures the vars.
- // 2. decldepth++ to denote loop body.
- // 3. typecheck body.
- // 4. decldepth--.
- typecheckrangeExpr(n)
-
- // second half of dance, the first half being typecheckrangeExpr
- n.SetTypecheck(1)
- ls := n.Vars
- for i1, n1 := range ls {
- if n1.Typecheck() == 0 {
- ls[i1] = AssignExpr(ls[i1])
- }
- }
-
- decldepth++
- Stmts(n.Body)
- decldepth--
-}
-
func typecheckrangeExpr(n *ir.RangeStmt) {
n.X = Expr(n.X)
@@ -136,8 +109,326 @@ func typecheckrangeExpr(n *ir.RangeStmt) {
}
}
+// type check assignment.
+// if this assignment is the definition of a var on the left side,
+// fill in the var's type.
+func tcAssign(n *ir.AssignStmt) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheckas", n)(nil)
+ }
+
+ // delicate little dance.
+ // the definition of n may refer to this assignment
+ // as its definition, in which case it will call typecheckas.
+ // in that case, do not call typecheck back, or it will cycle.
+ // if the variable has a type (ntype) then typechecking
+ // will not look at defn, so it is okay (and desirable,
+ // so that the conversion below happens).
+ n.X = Resolve(n.X)
+
+ if !ir.DeclaredBy(n.X, n) || n.X.Name().Ntype != nil {
+ n.X = AssignExpr(n.X)
+ }
+
+ // Use ctxMultiOK so we can emit an "N variables but M values" error
+ // to be consistent with typecheckas2 (#26616).
+ n.Y = typecheck(n.Y, ctxExpr|ctxMultiOK)
+ checkassign(n, n.X)
+ if n.Y != nil && n.Y.Type() != nil {
+ if n.Y.Type().IsFuncArgStruct() {
+ base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Y.(*ir.CallExpr).X, n.Y.Type().NumFields())
+ // Multi-value RHS isn't actually valid for OAS; nil out
+ // to indicate failed typechecking.
+ n.Y.SetType(nil)
+ } else if n.X.Type() != nil {
+ n.Y = AssignConv(n.Y, n.X.Type(), "assignment")
+ }
+ }
+
+ if ir.DeclaredBy(n.X, n) && n.X.Name().Ntype == nil {
+ n.Y = DefaultLit(n.Y, nil)
+ n.X.SetType(n.Y.Type())
+ }
+
+ // second half of dance.
+ // now that right is done, typecheck the left
+ // just to get it over with. see dance above.
+ n.SetTypecheck(1)
+
+ if n.X.Typecheck() == 0 {
+ n.X = AssignExpr(n.X)
+ }
+ if !ir.IsBlank(n.X) {
+ types.CheckSize(n.X.Type()) // ensure width is calculated for backend
+ }
+}
+
+func tcAssignList(n *ir.AssignListStmt) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheckas2", n)(nil)
+ }
+
+ ls := n.Lhs
+ for i1, n1 := range ls {
+ // delicate little dance.
+ n1 = Resolve(n1)
+ ls[i1] = n1
+
+ if !ir.DeclaredBy(n1, n) || n1.Name().Ntype != nil {
+ ls[i1] = AssignExpr(ls[i1])
+ }
+ }
+
+ cl := len(n.Lhs)
+ cr := len(n.Rhs)
+ if cl > 1 && cr == 1 {
+ n.Rhs[0] = typecheck(n.Rhs[0], ctxExpr|ctxMultiOK)
+ } else {
+ Exprs(n.Rhs)
+ }
+ checkassignlist(n, n.Lhs)
+
+ var l ir.Node
+ var r ir.Node
+ if cl == cr {
+ // easy
+ ls := n.Lhs
+ rs := n.Rhs
+ for il, nl := range ls {
+ nr := rs[il]
+ if nl.Type() != nil && nr.Type() != nil {
+ rs[il] = AssignConv(nr, nl.Type(), "assignment")
+ }
+ if ir.DeclaredBy(nl, n) && nl.Name().Ntype == nil {
+ rs[il] = DefaultLit(rs[il], nil)
+ nl.SetType(rs[il].Type())
+ }
+ }
+
+ goto out
+ }
+
+ l = n.Lhs[0]
+ r = n.Rhs[0]
+
+ // x,y,z = f()
+ if cr == 1 {
+ if r.Type() == nil {
+ goto out
+ }
+ switch r.Op() {
+ case ir.OCALLMETH, ir.OCALLINTER, ir.OCALLFUNC:
+ if !r.Type().IsFuncArgStruct() {
+ break
+ }
+ cr = r.Type().NumFields()
+ if cr != cl {
+ goto mismatch
+ }
+ r.(*ir.CallExpr).Use = ir.CallUseList
+ n.SetOp(ir.OAS2FUNC)
+ for i, l := range n.Lhs {
+ f := r.Type().Field(i)
+ if f.Type != nil && l.Type() != nil {
+ checkassignto(f.Type, l)
+ }
+ if ir.DeclaredBy(l, n) && l.Name().Ntype == nil {
+ l.SetType(f.Type)
+ }
+ }
+ goto out
+ }
+ }
+
+ // x, ok = y
+ if cl == 2 && cr == 1 {
+ if r.Type() == nil {
+ goto out
+ }
+ switch r.Op() {
+ case ir.OINDEXMAP, ir.ORECV, ir.ODOTTYPE:
+ switch r.Op() {
+ case ir.OINDEXMAP:
+ n.SetOp(ir.OAS2MAPR)
+ case ir.ORECV:
+ n.SetOp(ir.OAS2RECV)
+ case ir.ODOTTYPE:
+ r := r.(*ir.TypeAssertExpr)
+ n.SetOp(ir.OAS2DOTTYPE)
+ r.SetOp(ir.ODOTTYPE2)
+ }
+ if l.Type() != nil {
+ checkassignto(r.Type(), l)
+ }
+ if ir.DeclaredBy(l, n) {
+ l.SetType(r.Type())
+ }
+ l := n.Lhs[1]
+ if l.Type() != nil && !l.Type().IsBoolean() {
+ checkassignto(types.Types[types.TBOOL], l)
+ }
+ if ir.DeclaredBy(l, n) && l.Name().Ntype == nil {
+ l.SetType(types.Types[types.TBOOL])
+ }
+ goto out
+ }
+ }
+
+mismatch:
+ switch r.Op() {
+ default:
+ base.Errorf("assignment mismatch: %d variables but %d values", cl, cr)
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ r := r.(*ir.CallExpr)
+ base.Errorf("assignment mismatch: %d variables but %v returns %d values", cl, r.X, cr)
+ }
+
+ // second half of dance
+out:
+ n.SetTypecheck(1)
+ ls = n.Lhs
+ for i1, n1 := range ls {
+ if n1.Typecheck() == 0 {
+ ls[i1] = AssignExpr(ls[i1])
+ }
+ }
+}
+
+// tcFor typechecks an OFOR node.
+func tcFor(n *ir.ForStmt) ir.Node {
+ Stmts(n.Init())
+ decldepth++
+ n.Cond = Expr(n.Cond)
+ n.Cond = DefaultLit(n.Cond, nil)
+ if n.Cond != nil {
+ t := n.Cond.Type()
+ if t != nil && !t.IsBoolean() {
+ base.Errorf("non-bool %L used as for condition", n.Cond)
+ }
+ }
+ n.Post = Stmt(n.Post)
+ if n.Op() == ir.OFORUNTIL {
+ Stmts(n.Late)
+ }
+ Stmts(n.Body)
+ decldepth--
+ return n
+}
+
+func tcGoDefer(n *ir.GoDeferStmt) {
+ what := "defer"
+ if n.Op() == ir.OGO {
+ what = "go"
+ }
+
+ switch n.Call.Op() {
+ // ok
+ case ir.OCALLINTER,
+ ir.OCALLMETH,
+ ir.OCALLFUNC,
+ ir.OCLOSE,
+ ir.OCOPY,
+ ir.ODELETE,
+ ir.OPANIC,
+ ir.OPRINT,
+ ir.OPRINTN,
+ ir.ORECOVER:
+ return
+
+ case ir.OAPPEND,
+ ir.OCAP,
+ ir.OCOMPLEX,
+ ir.OIMAG,
+ ir.OLEN,
+ ir.OMAKE,
+ ir.OMAKESLICE,
+ ir.OMAKECHAN,
+ ir.OMAKEMAP,
+ ir.ONEW,
+ ir.OREAL,
+ ir.OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
+ if orig := ir.Orig(n.Call); orig.Op() == ir.OCONV {
+ break
+ }
+ base.ErrorfAt(n.Pos(), "%s discards result of %v", what, n.Call)
+ return
+ }
+
+ // type is broken or missing, most likely a method call on a broken type
+ // we will warn about the broken type elsewhere. no need to emit a potentially confusing error
+ if n.Call.Type() == nil || n.Call.Type().Broke() {
+ return
+ }
+
+ if !n.Diag() {
+ // The syntax made sure it was a call, so this must be
+ // a conversion.
+ n.SetDiag(true)
+ base.ErrorfAt(n.Pos(), "%s requires function call, not conversion", what)
+ }
+}
+
+// tcIf typechecks an OIF node.
+func tcIf(n *ir.IfStmt) ir.Node {
+ Stmts(n.Init())
+ n.Cond = Expr(n.Cond)
+ n.Cond = DefaultLit(n.Cond, nil)
+ if n.Cond != nil {
+ t := n.Cond.Type()
+ if t != nil && !t.IsBoolean() {
+ base.Errorf("non-bool %L used as if condition", n.Cond)
+ }
+ }
+ Stmts(n.Body)
+ Stmts(n.Else)
+ return n
+}
+
+// range
+func tcRange(n *ir.RangeStmt) {
+ // Typechecking order is important here:
+ // 0. first typecheck range expression (slice/map/chan),
+ // it is evaluated only once and so logically it is not part of the loop.
+ // 1. typecheck produced values,
+ // this part can declare new vars and so it must be typechecked before body,
+ // because body can contain a closure that captures the vars.
+ // 2. decldepth++ to denote loop body.
+ // 3. typecheck body.
+ // 4. decldepth--.
+ typecheckrangeExpr(n)
+
+ // second half of dance, the first half being typecheckrangeExpr
+ n.SetTypecheck(1)
+ ls := n.Vars
+ for i1, n1 := range ls {
+ if n1.Typecheck() == 0 {
+ ls[i1] = AssignExpr(ls[i1])
+ }
+ }
+
+ decldepth++
+ Stmts(n.Body)
+ decldepth--
+}
+
+// tcReturn typechecks an ORETURN node.
+func tcReturn(n *ir.ReturnStmt) ir.Node {
+ typecheckargs(n)
+ if ir.CurFunc == nil {
+ base.Errorf("return outside function")
+ n.SetType(nil)
+ return n
+ }
+
+ if ir.HasNamedResults(ir.CurFunc) && len(n.Results) == 0 {
+ return n
+ }
+ typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, func() string { return "return argument" })
+ return n
+}
+
// select
-func typecheckselect(sel *ir.SelectStmt) {
+func tcSelect(sel *ir.SelectStmt) {
var def ir.Node
lno := ir.SetPos(sel)
Stmts(sel.Init())
@@ -219,35 +510,43 @@ func typecheckselect(sel *ir.SelectStmt) {
base.Pos = lno
}
-type typeSet struct {
- m map[string][]typeSetEntry
-}
+// tcSend typechecks an OSEND node.
+func tcSend(n *ir.SendStmt) ir.Node {
+ n.Chan = Expr(n.Chan)
+ n.Value = Expr(n.Value)
+ n.Chan = DefaultLit(n.Chan, nil)
+ t := n.Chan.Type()
+ if t == nil {
+ return n
+ }
+ if !t.IsChan() {
+ base.Errorf("invalid operation: %v (send to non-chan type %v)", n, t)
+ return n
+ }
-func (s *typeSet) add(pos src.XPos, typ *types.Type) {
- if s.m == nil {
- s.m = make(map[string][]typeSetEntry)
+ if !t.ChanDir().CanSend() {
+ base.Errorf("invalid operation: %v (send to receive-only type %v)", n, t)
+ return n
}
- // LongString does not uniquely identify types, so we need to
- // disambiguate collisions with types.Identical.
- // TODO(mdempsky): Add a method that *is* unique.
- ls := typ.LongString()
- prevs := s.m[ls]
- for _, prev := range prevs {
- if types.Identical(typ, prev.typ) {
- base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos))
- return
- }
+ n.Value = AssignConv(n.Value, t.Elem(), "send")
+ if n.Value.Type() == nil {
+ return n
}
- s.m[ls] = append(prevs, typeSetEntry{pos, typ})
+ return n
}
-type typeSetEntry struct {
- pos src.XPos
- typ *types.Type
+// tcSwitch typechecks a switch statement.
+func tcSwitch(n *ir.SwitchStmt) {
+ Stmts(n.Init())
+ if n.Tag != nil && n.Tag.Op() == ir.OTYPESW {
+ tcSwitchType(n)
+ } else {
+ tcSwitchExpr(n)
+ }
}
-func typecheckExprSwitch(n *ir.SwitchStmt) {
+func tcSwitchExpr(n *ir.SwitchStmt) {
t := types.Types[types.TBOOL]
if n.Tag != nil {
n.Tag = Expr(n.Tag)
@@ -328,7 +627,7 @@ func typecheckExprSwitch(n *ir.SwitchStmt) {
}
}
-func typecheckTypeSwitch(n *ir.SwitchStmt) {
+func tcSwitchType(n *ir.SwitchStmt) {
guard := n.Tag.(*ir.TypeSwitchGuard)
guard.X = Expr(guard.X)
t := guard.X.Type()
@@ -358,7 +657,7 @@ func typecheckTypeSwitch(n *ir.SwitchStmt) {
}
for i := range ls {
- ls[i] = check(ls[i], ctxExpr|ctxType)
+ ls[i] = typecheck(ls[i], ctxExpr|ctxType)
n1 := ls[i]
if t == nil || n1.Type() == nil {
continue
@@ -424,12 +723,30 @@ func typecheckTypeSwitch(n *ir.SwitchStmt) {
}
}
-// typecheckswitch typechecks a switch statement.
-func typecheckswitch(n *ir.SwitchStmt) {
- Stmts(n.Init())
- if n.Tag != nil && n.Tag.Op() == ir.OTYPESW {
- typecheckTypeSwitch(n)
- } else {
- typecheckExprSwitch(n)
+type typeSet struct {
+ m map[string][]typeSetEntry
+}
+
+type typeSetEntry struct {
+ pos src.XPos
+ typ *types.Type
+}
+
+func (s *typeSet) add(pos src.XPos, typ *types.Type) {
+ if s.m == nil {
+ s.m = make(map[string][]typeSetEntry)
}
+
+ // LongString does not uniquely identify types, so we need to
+ // disambiguate collisions with types.Identical.
+ // TODO(mdempsky): Add a method that *is* unique.
+ ls := typ.LongString()
+ prevs := s.m[ls]
+ for _, prev := range prevs {
+ if types.Identical(typ, prev.typ) {
+ base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos))
+ return
+ }
+ }
+ s.m[ls] = append(prevs, typeSetEntry{pos, typ})
}