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