Source file
src/go/types/stmt.go
1
2
3
4
5
6
7 package types
8
9 import (
10 "go/ast"
11 "go/constant"
12 "go/token"
13 "sort"
14 )
15
16 func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) {
17 if check.conf.IgnoreFuncBodies {
18 panic("function body not ignored")
19 }
20
21 if trace {
22 check.trace(body.Pos(), "-- %s: %s", name, sig)
23 }
24
25
26 sig.scope.pos = body.Pos()
27 sig.scope.end = body.End()
28
29
30
31 defer func(env environment, indent int) {
32 check.environment = env
33 check.indent = indent
34 }(check.environment, check.indent)
35 check.environment = environment{
36 decl: decl,
37 scope: sig.scope,
38 iota: iota,
39 sig: sig,
40 }
41 check.indent = 0
42
43 check.stmtList(0, body.List)
44
45 if check.hasLabel {
46 check.labels(body)
47 }
48
49 if sig.results.Len() > 0 && !check.isTerminating(body, "") {
50 check.error(atPos(body.Rbrace), _MissingReturn, "missing return")
51 }
52
53
54
55 check.usage(sig.scope)
56 }
57
58 func (check *Checker) usage(scope *Scope) {
59 var unused []*Var
60 for name, elem := range scope.elems {
61 elem = resolve(name, elem)
62 if v, _ := elem.(*Var); v != nil && !v.used {
63 unused = append(unused, v)
64 }
65 }
66 sort.Slice(unused, func(i, j int) bool {
67 return unused[i].pos < unused[j].pos
68 })
69 for _, v := range unused {
70 check.softErrorf(v, _UnusedVar, "%s declared but not used", v.name)
71 }
72
73 for _, scope := range scope.children {
74
75
76 if !scope.isFunc {
77 check.usage(scope)
78 }
79 }
80 }
81
82
83
84
85
86 type stmtContext uint
87
88 const (
89
90 breakOk stmtContext = 1 << iota
91 continueOk
92 fallthroughOk
93
94
95 finalSwitchCase
96 inTypeSwitch
97 )
98
99 func (check *Checker) simpleStmt(s ast.Stmt) {
100 if s != nil {
101 check.stmt(0, s)
102 }
103 }
104
105 func trimTrailingEmptyStmts(list []ast.Stmt) []ast.Stmt {
106 for i := len(list); i > 0; i-- {
107 if _, ok := list[i-1].(*ast.EmptyStmt); !ok {
108 return list[:i]
109 }
110 }
111 return nil
112 }
113
114 func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
115 ok := ctxt&fallthroughOk != 0
116 inner := ctxt &^ fallthroughOk
117 list = trimTrailingEmptyStmts(list)
118 for i, s := range list {
119 inner := inner
120 if ok && i+1 == len(list) {
121 inner |= fallthroughOk
122 }
123 check.stmt(inner, s)
124 }
125 }
126
127 func (check *Checker) multipleDefaults(list []ast.Stmt) {
128 var first ast.Stmt
129 for _, s := range list {
130 var d ast.Stmt
131 switch c := s.(type) {
132 case *ast.CaseClause:
133 if len(c.List) == 0 {
134 d = s
135 }
136 case *ast.CommClause:
137 if c.Comm == nil {
138 d = s
139 }
140 default:
141 check.invalidAST(s, "case/communication clause expected")
142 }
143 if d != nil {
144 if first != nil {
145 check.errorf(d, _DuplicateDefault, "multiple defaults (first at %s)", check.fset.Position(first.Pos()))
146 } else {
147 first = d
148 }
149 }
150 }
151 }
152
153 func (check *Checker) openScope(node ast.Node, comment string) {
154 scope := NewScope(check.scope, node.Pos(), node.End(), comment)
155 check.recordScope(node, scope)
156 check.scope = scope
157 }
158
159 func (check *Checker) closeScope() {
160 check.scope = check.scope.Parent()
161 }
162
163 func assignOp(op token.Token) token.Token {
164
165 if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN {
166 return op + (token.ADD - token.ADD_ASSIGN)
167 }
168 return token.ILLEGAL
169 }
170
171 func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
172 var x operand
173 var msg string
174 var code errorCode
175 switch check.rawExpr(&x, call, nil, false) {
176 case conversion:
177 msg = "requires function call, not conversion"
178 code = _InvalidDefer
179 if keyword == "go" {
180 code = _InvalidGo
181 }
182 case expression:
183 msg = "discards result of"
184 code = _UnusedResults
185 case statement:
186 return
187 default:
188 unreachable()
189 }
190 check.errorf(&x, code, "%s %s %s", keyword, msg, &x)
191 }
192
193
194 func goVal(val constant.Value) any {
195
196 if val == nil {
197 return nil
198 }
199
200
201
202
203 switch val.Kind() {
204 case constant.Int:
205 if x, ok := constant.Int64Val(val); ok {
206 return x
207 }
208 if x, ok := constant.Uint64Val(val); ok {
209 return x
210 }
211 case constant.Float:
212 if x, ok := constant.Float64Val(val); ok {
213 return x
214 }
215 case constant.String:
216 return constant.StringVal(val)
217 }
218 return nil
219 }
220
221
222
223
224
225
226
227 type (
228 valueMap map[any][]valueType
229 valueType struct {
230 pos token.Pos
231 typ Type
232 }
233 )
234
235 func (check *Checker) caseValues(x *operand, values []ast.Expr, seen valueMap) {
236 L:
237 for _, e := range values {
238 var v operand
239 check.expr(&v, e)
240 if x.mode == invalid || v.mode == invalid {
241 continue L
242 }
243 check.convertUntyped(&v, x.typ)
244 if v.mode == invalid {
245 continue L
246 }
247
248 res := v
249 check.comparison(&res, x, token.EQL, true)
250 if res.mode == invalid {
251 continue L
252 }
253 if v.mode != constant_ {
254 continue L
255 }
256
257 if val := goVal(v.val); val != nil {
258
259
260 for _, vt := range seen[val] {
261 if Identical(v.typ, vt.typ) {
262 check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
263 check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case")
264 continue L
265 }
266 }
267 seen[val] = append(seen[val], valueType{v.Pos(), v.typ})
268 }
269 }
270 }
271
272
273 func (check *Checker) isNil(e ast.Expr) bool {
274
275 if name, _ := unparen(e).(*ast.Ident); name != nil {
276 _, ok := check.lookup(name.Name).(*Nil)
277 return ok
278 }
279 return false
280 }
281
282
283 func (check *Checker) caseTypes(x *operand, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
284 var dummy operand
285 L:
286 for _, e := range types {
287
288 if check.isNil(e) {
289 T = nil
290 check.expr(&dummy, e)
291 } else {
292 T = check.varType(e)
293 if T == Typ[Invalid] {
294 continue L
295 }
296 }
297
298
299 for t, other := range seen {
300 if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
301
302 Ts := "nil"
303 if T != nil {
304 Ts = TypeString(T, check.qualifier)
305 }
306 check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
307 check.error(other, _DuplicateCase, "\tprevious case")
308 continue L
309 }
310 }
311 seen[T] = e
312 if x != nil && T != nil {
313 check.typeAssertion(e, x, T, true)
314 }
315 }
316 return
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361 func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
362
363 if debug {
364 defer func(scope *Scope) {
365
366 if p := recover(); p != nil {
367 panic(p)
368 }
369 assert(scope == check.scope)
370 }(check.scope)
371 }
372
373
374 defer check.processDelayed(len(check.delayed))
375
376
377 inner := ctxt &^ (fallthroughOk | finalSwitchCase | inTypeSwitch)
378
379 switch s := s.(type) {
380 case *ast.BadStmt, *ast.EmptyStmt:
381
382
383 case *ast.DeclStmt:
384 check.declStmt(s.Decl)
385
386 case *ast.LabeledStmt:
387 check.hasLabel = true
388 check.stmt(ctxt, s.Stmt)
389
390 case *ast.ExprStmt:
391
392
393
394 var x operand
395 kind := check.rawExpr(&x, s.X, nil, false)
396 var msg string
397 var code errorCode
398 switch x.mode {
399 default:
400 if kind == statement {
401 return
402 }
403 msg = "is not used"
404 code = _UnusedExpr
405 case builtin:
406 msg = "must be called"
407 code = _UncalledBuiltin
408 case typexpr:
409 msg = "is not an expression"
410 code = _NotAnExpr
411 }
412 check.errorf(&x, code, "%s %s", &x, msg)
413
414 case *ast.SendStmt:
415 var ch, val operand
416 check.expr(&ch, s.Chan)
417 check.expr(&val, s.Value)
418 if ch.mode == invalid || val.mode == invalid {
419 return
420 }
421 u := coreType(ch.typ)
422 if u == nil {
423 check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no core type", &ch)
424 return
425 }
426 uch, _ := u.(*Chan)
427 if uch == nil {
428 check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
429 return
430 }
431 if uch.dir == RecvOnly {
432 check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch)
433 return
434 }
435 check.assignment(&val, uch.elem, "send")
436
437 case *ast.IncDecStmt:
438 var op token.Token
439 switch s.Tok {
440 case token.INC:
441 op = token.ADD
442 case token.DEC:
443 op = token.SUB
444 default:
445 check.invalidAST(inNode(s, s.TokPos), "unknown inc/dec operation %s", s.Tok)
446 return
447 }
448
449 var x operand
450 check.expr(&x, s.X)
451 if x.mode == invalid {
452 return
453 }
454 if !allNumeric(x.typ) {
455 check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
456 return
457 }
458
459 Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"}
460 check.binary(&x, nil, s.X, Y, op, s.TokPos)
461 if x.mode == invalid {
462 return
463 }
464 check.assignVar(s.X, &x)
465
466 case *ast.AssignStmt:
467 switch s.Tok {
468 case token.ASSIGN, token.DEFINE:
469 if len(s.Lhs) == 0 {
470 check.invalidAST(s, "missing lhs in assignment")
471 return
472 }
473 if s.Tok == token.DEFINE {
474 check.shortVarDecl(inNode(s, s.TokPos), s.Lhs, s.Rhs)
475 } else {
476
477 check.assignVars(s.Lhs, s.Rhs)
478 }
479
480 default:
481
482 if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
483 check.errorf(inNode(s, s.TokPos), _MultiValAssignOp, "assignment operation %s requires single-valued expressions", s.Tok)
484 return
485 }
486 op := assignOp(s.Tok)
487 if op == token.ILLEGAL {
488 check.invalidAST(atPos(s.TokPos), "unknown assignment operation %s", s.Tok)
489 return
490 }
491 var x operand
492 check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op, s.TokPos)
493 if x.mode == invalid {
494 return
495 }
496 check.assignVar(s.Lhs[0], &x)
497 }
498
499 case *ast.GoStmt:
500 check.suspendedCall("go", s.Call)
501
502 case *ast.DeferStmt:
503 check.suspendedCall("defer", s.Call)
504
505 case *ast.ReturnStmt:
506 res := check.sig.results
507
508
509 if len(s.Results) == 0 && res.Len() > 0 && res.vars[0].name != "" {
510
511
512
513 for _, obj := range res.vars {
514 if alt := check.lookup(obj.name); alt != nil && alt != obj {
515 check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
516 check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
517
518 }
519 }
520 } else {
521 var lhs []*Var
522 if res.Len() > 0 {
523 lhs = res.vars
524 }
525 check.initVars(lhs, s.Results, s)
526 }
527
528 case *ast.BranchStmt:
529 if s.Label != nil {
530 check.hasLabel = true
531 return
532 }
533 switch s.Tok {
534 case token.BREAK:
535 if ctxt&breakOk == 0 {
536 check.error(s, _MisplacedBreak, "break not in for, switch, or select statement")
537 }
538 case token.CONTINUE:
539 if ctxt&continueOk == 0 {
540 check.error(s, _MisplacedContinue, "continue not in for statement")
541 }
542 case token.FALLTHROUGH:
543 if ctxt&fallthroughOk == 0 {
544 var msg string
545 switch {
546 case ctxt&finalSwitchCase != 0:
547 msg = "cannot fallthrough final case in switch"
548 case ctxt&inTypeSwitch != 0:
549 msg = "cannot fallthrough in type switch"
550 default:
551 msg = "fallthrough statement out of place"
552 }
553 check.error(s, _MisplacedFallthrough, msg)
554 }
555 default:
556 check.invalidAST(s, "branch statement: %s", s.Tok)
557 }
558
559 case *ast.BlockStmt:
560 check.openScope(s, "block")
561 defer check.closeScope()
562
563 check.stmtList(inner, s.List)
564
565 case *ast.IfStmt:
566 check.openScope(s, "if")
567 defer check.closeScope()
568
569 check.simpleStmt(s.Init)
570 var x operand
571 check.expr(&x, s.Cond)
572 if x.mode != invalid && !allBoolean(x.typ) {
573 check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement")
574 }
575 check.stmt(inner, s.Body)
576
577
578 switch s.Else.(type) {
579 case nil, *ast.BadStmt:
580
581 case *ast.IfStmt, *ast.BlockStmt:
582 check.stmt(inner, s.Else)
583 default:
584 check.invalidAST(s.Else, "invalid else branch in if statement")
585 }
586
587 case *ast.SwitchStmt:
588 inner |= breakOk
589 check.openScope(s, "switch")
590 defer check.closeScope()
591
592 check.simpleStmt(s.Init)
593 var x operand
594 if s.Tag != nil {
595 check.expr(&x, s.Tag)
596
597
598 check.assignment(&x, nil, "switch expression")
599 if x.mode != invalid && !Comparable(x.typ) && !hasNil(x.typ) {
600 check.errorf(&x, _InvalidExprSwitch, "cannot switch on %s (%s is not comparable)", &x, x.typ)
601 x.mode = invalid
602 }
603 } else {
604
605
606 x.mode = constant_
607 x.typ = Typ[Bool]
608 x.val = constant.MakeBool(true)
609 x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
610 }
611
612 check.multipleDefaults(s.Body.List)
613
614 seen := make(valueMap)
615 for i, c := range s.Body.List {
616 clause, _ := c.(*ast.CaseClause)
617 if clause == nil {
618 check.invalidAST(c, "incorrect expression switch case")
619 continue
620 }
621 check.caseValues(&x, clause.List, seen)
622 check.openScope(clause, "case")
623 inner := inner
624 if i+1 < len(s.Body.List) {
625 inner |= fallthroughOk
626 } else {
627 inner |= finalSwitchCase
628 }
629 check.stmtList(inner, clause.Body)
630 check.closeScope()
631 }
632
633 case *ast.TypeSwitchStmt:
634 inner |= breakOk | inTypeSwitch
635 check.openScope(s, "type switch")
636 defer check.closeScope()
637
638 check.simpleStmt(s.Init)
639
640
641
642
643
644
645
646
647
648 var lhs *ast.Ident
649 var rhs ast.Expr
650 switch guard := s.Assign.(type) {
651 case *ast.ExprStmt:
652 rhs = guard.X
653 case *ast.AssignStmt:
654 if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
655 check.invalidAST(s, "incorrect form of type switch guard")
656 return
657 }
658
659 lhs, _ = guard.Lhs[0].(*ast.Ident)
660 if lhs == nil {
661 check.invalidAST(s, "incorrect form of type switch guard")
662 return
663 }
664
665 if lhs.Name == "_" {
666
667 check.softErrorf(lhs, _NoNewVar, "no new variable on left side of :=")
668 lhs = nil
669 } else {
670 check.recordDef(lhs, nil)
671 }
672
673 rhs = guard.Rhs[0]
674
675 default:
676 check.invalidAST(s, "incorrect form of type switch guard")
677 return
678 }
679
680
681 expr, _ := rhs.(*ast.TypeAssertExpr)
682 if expr == nil || expr.Type != nil {
683 check.invalidAST(s, "incorrect form of type switch guard")
684 return
685 }
686 var x operand
687 check.expr(&x, expr.X)
688 if x.mode == invalid {
689 return
690 }
691
692 var sx *operand
693 if isTypeParam(x.typ) {
694 check.errorf(&x, _InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &x)
695 } else {
696 if _, ok := under(x.typ).(*Interface); ok {
697 sx = &x
698 } else {
699 check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
700 }
701 }
702
703 check.multipleDefaults(s.Body.List)
704
705 var lhsVars []*Var
706 seen := make(map[Type]ast.Expr)
707 for _, s := range s.Body.List {
708 clause, _ := s.(*ast.CaseClause)
709 if clause == nil {
710 check.invalidAST(s, "incorrect type switch case")
711 continue
712 }
713
714 T := check.caseTypes(sx, clause.List, seen)
715 check.openScope(clause, "case")
716
717 if lhs != nil {
718
719
720
721
722
723 if len(clause.List) != 1 || T == nil {
724 T = x.typ
725 }
726 obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
727 scopePos := clause.Pos() + token.Pos(len("default"))
728 if n := len(clause.List); n > 0 {
729 scopePos = clause.List[n-1].End()
730 }
731 check.declare(check.scope, nil, obj, scopePos)
732 check.recordImplicit(clause, obj)
733
734
735
736 lhsVars = append(lhsVars, obj)
737 }
738 check.stmtList(inner, clause.Body)
739 check.closeScope()
740 }
741
742
743 if lhs != nil {
744 var used bool
745 for _, v := range lhsVars {
746 if v.used {
747 used = true
748 }
749 v.used = true
750 }
751 if !used {
752 check.softErrorf(lhs, _UnusedVar, "%s declared but not used", lhs.Name)
753 }
754 }
755
756 case *ast.SelectStmt:
757 inner |= breakOk
758
759 check.multipleDefaults(s.Body.List)
760
761 for _, s := range s.Body.List {
762 clause, _ := s.(*ast.CommClause)
763 if clause == nil {
764 continue
765 }
766
767
768 valid := false
769 var rhs ast.Expr
770 switch s := clause.Comm.(type) {
771 case nil, *ast.SendStmt:
772 valid = true
773 case *ast.AssignStmt:
774 if len(s.Rhs) == 1 {
775 rhs = s.Rhs[0]
776 }
777 case *ast.ExprStmt:
778 rhs = s.X
779 }
780
781
782 if rhs != nil {
783 if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
784 valid = true
785 }
786 }
787
788 if !valid {
789 check.error(clause.Comm, _InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
790 continue
791 }
792
793 check.openScope(s, "case")
794 if clause.Comm != nil {
795 check.stmt(inner, clause.Comm)
796 }
797 check.stmtList(inner, clause.Body)
798 check.closeScope()
799 }
800
801 case *ast.ForStmt:
802 inner |= breakOk | continueOk
803 check.openScope(s, "for")
804 defer check.closeScope()
805
806 check.simpleStmt(s.Init)
807 if s.Cond != nil {
808 var x operand
809 check.expr(&x, s.Cond)
810 if x.mode != invalid && !allBoolean(x.typ) {
811 check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement")
812 }
813 }
814 check.simpleStmt(s.Post)
815
816
817 if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
818 check.softErrorf(s, _InvalidPostDecl, "cannot declare in post statement")
819
820
821
822 check.use(s.Lhs...)
823 }
824 check.stmt(inner, s.Body)
825
826 case *ast.RangeStmt:
827 inner |= breakOk | continueOk
828
829
830 var x operand
831 check.expr(&x, s.X)
832
833
834 var key, val Type
835 if x.mode != invalid {
836
837 var cause string
838 u := coreType(x.typ)
839 switch t := u.(type) {
840 case nil:
841 cause = check.sprintf("%s has no core type", x.typ)
842 case *Chan:
843 if s.Value != nil {
844 check.softErrorf(s.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &x)
845
846 }
847 if t.dir == SendOnly {
848 cause = "receive from send-only channel"
849 }
850 }
851 key, val = rangeKeyVal(u)
852 if key == nil || cause != "" {
853 if cause == "" {
854 check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
855 } else {
856 check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s (%s)", &x, cause)
857 }
858
859 }
860 }
861
862
863
864 check.openScope(s, "range")
865 defer check.closeScope()
866
867
868
869
870
871 lhs := [2]ast.Expr{s.Key, s.Value}
872 rhs := [2]Type{key, val}
873
874 if s.Tok == token.DEFINE {
875
876 var vars []*Var
877 for i, lhs := range lhs {
878 if lhs == nil {
879 continue
880 }
881
882
883 var obj *Var
884 if ident, _ := lhs.(*ast.Ident); ident != nil {
885
886 name := ident.Name
887 obj = NewVar(ident.Pos(), check.pkg, name, nil)
888 check.recordDef(ident, obj)
889
890 if name != "_" {
891 vars = append(vars, obj)
892 }
893 } else {
894 check.invalidAST(lhs, "cannot declare %s", lhs)
895 obj = NewVar(lhs.Pos(), check.pkg, "_", nil)
896 }
897
898
899 if typ := rhs[i]; typ != nil {
900 x.mode = value
901 x.expr = lhs
902 x.typ = typ
903 check.initVar(obj, &x, "range clause")
904 } else {
905 obj.typ = Typ[Invalid]
906 obj.used = true
907 }
908 }
909
910
911 if len(vars) > 0 {
912 scopePos := s.Body.Pos()
913 for _, obj := range vars {
914 check.declare(check.scope, nil , obj, scopePos)
915 }
916 } else {
917 check.error(inNode(s, s.TokPos), _NoNewVar, "no new variables on left side of :=")
918 }
919 } else {
920
921 for i, lhs := range lhs {
922 if lhs == nil {
923 continue
924 }
925 if typ := rhs[i]; typ != nil {
926 x.mode = value
927 x.expr = lhs
928 x.typ = typ
929 check.assignVar(lhs, &x)
930 }
931 }
932 }
933
934 check.stmt(inner, s.Body)
935
936 default:
937 check.invalidAST(s, "invalid statement")
938 }
939 }
940
941
942
943
944 func rangeKeyVal(typ Type) (key, val Type) {
945 switch typ := arrayPtrDeref(typ).(type) {
946 case *Basic:
947 if isString(typ) {
948 return Typ[Int], universeRune
949 }
950 case *Array:
951 return Typ[Int], typ.elem
952 case *Slice:
953 return Typ[Int], typ.elem
954 case *Map:
955 return typ.key, typ.elem
956 case *Chan:
957 return typ.elem, Typ[Invalid]
958 }
959 return
960 }
961
View as plain text