1 | // This file is split into two packages, old and new. |
---|---|
2 | // It is syntactically valid Go so that gofmt can process it. |
3 | // |
4 | // If a comment begins with: Then: |
5 | // old write subsequent lines to the "old" package |
6 | // new write subsequent lines to the "new" package |
7 | // both write subsequent lines to both packages |
8 | // c expect a compatible error with the following text |
9 | // i expect an incompatible error with the following text |
10 | package ignore |
11 | |
12 | // both |
13 | import "io" |
14 | |
15 | //////////////// Basics |
16 | |
17 | //// Same type in both: OK. |
18 | // both |
19 | type A int |
20 | |
21 | //// Changing the type is an incompatible change. |
22 | // old |
23 | type B int |
24 | |
25 | // new |
26 | // i B: changed from int to string |
27 | type B string |
28 | |
29 | //// Adding a new type, whether alias or not, is a compatible change. |
30 | // new |
31 | // c AA: added |
32 | type AA = A |
33 | |
34 | // c B1: added |
35 | type B1 bool |
36 | |
37 | //// Change of type for an unexported name doesn't matter... |
38 | // old |
39 | type t int |
40 | |
41 | // new |
42 | type t string // OK: t isn't part of the API |
43 | |
44 | //// ...unless it is exposed. |
45 | // both |
46 | var V2 u |
47 | |
48 | // old |
49 | type u string |
50 | |
51 | // new |
52 | // i u: changed from string to int |
53 | type u int |
54 | |
55 | //// An exposed, unexported type can be renamed. |
56 | // both |
57 | type u2 int |
58 | |
59 | // old |
60 | type u1 int |
61 | |
62 | var V5 u1 |
63 | |
64 | // new |
65 | var V5 u2 // OK: V5 has changed type, but old u1 corresopnds to new u2 |
66 | |
67 | //// Splitting a single type into two is an incompatible change. |
68 | // both |
69 | type u3 int |
70 | |
71 | // old |
72 | type ( |
73 | Split1 = u1 |
74 | Split2 = u1 |
75 | ) |
76 | |
77 | // new |
78 | type ( |
79 | Split1 = u2 // OK, since old u1 corresponds to new u2 |
80 | |
81 | // This tries to make u1 correspond to u3 |
82 | // i Split2: changed from u1 to u3 |
83 | Split2 = u3 |
84 | ) |
85 | |
86 | //// Merging two types into one is OK. |
87 | // old |
88 | type ( |
89 | GoodMerge1 = u2 |
90 | GoodMerge2 = u3 |
91 | ) |
92 | |
93 | // new |
94 | type ( |
95 | GoodMerge1 = u3 |
96 | GoodMerge2 = u3 |
97 | ) |
98 | |
99 | //// Merging isn't OK here because a method is lost. |
100 | // both |
101 | type u4 int |
102 | |
103 | func (u4) M() {} |
104 | |
105 | // old |
106 | type ( |
107 | BadMerge1 = u3 |
108 | BadMerge2 = u4 |
109 | ) |
110 | |
111 | // new |
112 | type ( |
113 | BadMerge1 = u3 |
114 | // i u4.M: removed |
115 | // What's really happening here is that old u4 corresponds to new u3, |
116 | // and new u3's method set is not a superset of old u4's. |
117 | BadMerge2 = u3 |
118 | ) |
119 | |
120 | // old |
121 | type Rem int |
122 | |
123 | // new |
124 | // i Rem: removed |
125 | |
126 | //////////////// Constants |
127 | |
128 | //// type changes |
129 | // old |
130 | const ( |
131 | C1 = 1 |
132 | C2 int = 2 |
133 | C3 = 3 |
134 | C4 u1 = 4 |
135 | ) |
136 | |
137 | var V8 int |
138 | |
139 | // new |
140 | const ( |
141 | // i C1: changed from untyped int to untyped string |
142 | C1 = "1" |
143 | // i C2: changed from int to untyped int |
144 | C2 = -1 |
145 | // i C3: changed from untyped int to int |
146 | C3 int = 3 |
147 | // i V8: changed from var to const |
148 | V8 int = 1 |
149 | C4 u2 = 4 // OK: u1 corresponds to u2 |
150 | ) |
151 | |
152 | // value change |
153 | // old |
154 | const ( |
155 | Cr1 = 1 |
156 | Cr2 = "2" |
157 | Cr3 = 3.5 |
158 | Cr4 = complex(0, 4.1) |
159 | ) |
160 | |
161 | // new |
162 | const ( |
163 | // i Cr1: value changed from 1 to -1 |
164 | Cr1 = -1 |
165 | // i Cr2: value changed from "2" to "3" |
166 | Cr2 = "3" |
167 | // i Cr3: value changed from 3.5 to 3.8 |
168 | Cr3 = 3.8 |
169 | // i Cr4: value changed from (0 + 4.1i) to (4.1 + 0i) |
170 | Cr4 = complex(4.1, 0) |
171 | ) |
172 | |
173 | //////////////// Variables |
174 | |
175 | //// simple type changes |
176 | // old |
177 | var ( |
178 | V1 string |
179 | V3 A |
180 | V7 <-chan int |
181 | ) |
182 | |
183 | // new |
184 | var ( |
185 | // i V1: changed from string to []string |
186 | V1 []string |
187 | V3 A // OK: same |
188 | // i V7: changed from <-chan int to chan int |
189 | V7 chan int |
190 | ) |
191 | |
192 | //// interface type changes |
193 | // old |
194 | var ( |
195 | V9 interface{ M() } |
196 | V10 interface{ M() } |
197 | V11 interface{ M() } |
198 | ) |
199 | |
200 | // new |
201 | var ( |
202 | // i V9: changed from interface{M()} to interface{} |
203 | V9 interface{} |
204 | // i V10: changed from interface{M()} to interface{M(); M2()} |
205 | V10 interface { |
206 | M2() |
207 | M() |
208 | } |
209 | // i V11: changed from interface{M()} to interface{M(int)} |
210 | V11 interface{ M(int) } |
211 | ) |
212 | |
213 | //// struct type changes |
214 | // old |
215 | var ( |
216 | VS1 struct{ A, B int } |
217 | VS2 struct{ A, B int } |
218 | VS3 struct{ A, B int } |
219 | VS4 struct { |
220 | A int |
221 | u1 |
222 | } |
223 | ) |
224 | |
225 | // new |
226 | var ( |
227 | // i VS1: changed from struct{A int; B int} to struct{B int; A int} |
228 | VS1 struct{ B, A int } |
229 | // i VS2: changed from struct{A int; B int} to struct{A int} |
230 | VS2 struct{ A int } |
231 | // i VS3: changed from struct{A int; B int} to struct{A int; B int; C int} |
232 | VS3 struct{ A, B, C int } |
233 | VS4 struct { |
234 | A int |
235 | u2 |
236 | } |
237 | ) |
238 | |
239 | //////////////// Types |
240 | |
241 | // old |
242 | const C5 = 3 |
243 | |
244 | type ( |
245 | A1 [1]int |
246 | A2 [2]int |
247 | A3 [C5]int |
248 | ) |
249 | |
250 | // new |
251 | // i C5: value changed from 3 to 4 |
252 | const C5 = 4 |
253 | |
254 | type ( |
255 | A1 [1]int |
256 | // i A2: changed from [2]int to [2]bool |
257 | A2 [2]bool |
258 | // i A3: changed from [3]int to [4]int |
259 | A3 [C5]int |
260 | ) |
261 | |
262 | // old |
263 | type ( |
264 | Sl []int |
265 | P1 *int |
266 | P2 *u1 |
267 | ) |
268 | |
269 | // new |
270 | type ( |
271 | // i Sl: changed from []int to []string |
272 | Sl []string |
273 | // i P1: changed from *int to **bool |
274 | P1 **bool |
275 | P2 *u2 // OK: u1 corresponds to u2 |
276 | ) |
277 | |
278 | // old |
279 | type Bc1 int32 |
280 | type Bc2 uint |
281 | type Bc3 float32 |
282 | type Bc4 complex64 |
283 | |
284 | // new |
285 | // c Bc1: changed from int32 to int |
286 | type Bc1 int |
287 | |
288 | // c Bc2: changed from uint to uint64 |
289 | type Bc2 uint64 |
290 | |
291 | // c Bc3: changed from float32 to float64 |
292 | type Bc3 float64 |
293 | |
294 | // c Bc4: changed from complex64 to complex128 |
295 | type Bc4 complex128 |
296 | |
297 | // old |
298 | type Bi1 int32 |
299 | type Bi2 uint |
300 | type Bi3 float64 |
301 | type Bi4 complex128 |
302 | |
303 | // new |
304 | // i Bi1: changed from int32 to int16 |
305 | type Bi1 int16 |
306 | |
307 | // i Bi2: changed from uint to uint32 |
308 | type Bi2 uint32 |
309 | |
310 | // i Bi3: changed from float64 to float32 |
311 | type Bi3 float32 |
312 | |
313 | // i Bi4: changed from complex128 to complex64 |
314 | type Bi4 complex64 |
315 | |
316 | // old |
317 | type ( |
318 | M1 map[string]int |
319 | M2 map[string]int |
320 | M3 map[string]int |
321 | ) |
322 | |
323 | // new |
324 | type ( |
325 | M1 map[string]int |
326 | // i M2: changed from map[string]int to map[int]int |
327 | M2 map[int]int |
328 | // i M3: changed from map[string]int to map[string]string |
329 | M3 map[string]string |
330 | ) |
331 | |
332 | // old |
333 | type ( |
334 | Ch1 chan int |
335 | Ch2 <-chan int |
336 | Ch3 chan int |
337 | Ch4 <-chan int |
338 | ) |
339 | |
340 | // new |
341 | type ( |
342 | // i Ch1, element type: changed from int to bool |
343 | Ch1 chan bool |
344 | // i Ch2: changed direction |
345 | Ch2 chan<- int |
346 | // i Ch3: changed direction |
347 | Ch3 <-chan int |
348 | // c Ch4: removed direction |
349 | Ch4 chan int |
350 | ) |
351 | |
352 | // old |
353 | type I1 interface { |
354 | M1() |
355 | M2() |
356 | } |
357 | |
358 | // new |
359 | type I1 interface { |
360 | // M1() |
361 | // i I1.M1: removed |
362 | M2(int) |
363 | // i I1.M2: changed from func() to func(int) |
364 | M3() |
365 | // i I1.M3: added |
366 | m() |
367 | // i I1.m: added unexported method |
368 | } |
369 | |
370 | // old |
371 | type I2 interface { |
372 | M1() |
373 | m() |
374 | } |
375 | |
376 | // new |
377 | type I2 interface { |
378 | M1() |
379 | // m() Removing an unexported method is OK. |
380 | m2() // OK, because old already had an unexported method |
381 | // c I2.M2: added |
382 | M2() |
383 | } |
384 | |
385 | // old |
386 | type I3 interface { |
387 | io.Reader |
388 | M() |
389 | } |
390 | |
391 | // new |
392 | // OK: what matters is the method set; the name of the embedded |
393 | // interface isn't important. |
394 | type I3 interface { |
395 | M() |
396 | Read([]byte) (int, error) |
397 | } |
398 | |
399 | // old |
400 | type I4 io.Writer |
401 | |
402 | // new |
403 | // OK: in both, I4 is a distinct type from io.Writer, and |
404 | // the old and new I4s have the same method set. |
405 | type I4 interface { |
406 | Write([]byte) (int, error) |
407 | } |
408 | |
409 | // old |
410 | type I5 = io.Writer |
411 | |
412 | // new |
413 | // i I5: changed from io.Writer to I5 |
414 | // In old, I5 and io.Writer are the same type; in new, |
415 | // they are different. That can break something like: |
416 | // var _ func(io.Writer) = func(pkg.I6) {} |
417 | type I5 io.Writer |
418 | |
419 | // old |
420 | type I6 interface{ Write([]byte) (int, error) } |
421 | |
422 | // new |
423 | // i I6: changed from I6 to io.Writer |
424 | // Similar to the above. |
425 | type I6 = io.Writer |
426 | |
427 | //// correspondence with a basic type |
428 | // Basic types are technically defined types, but they aren't |
429 | // represented that way in go/types, so the cases below are special. |
430 | |
431 | // both |
432 | type T1 int |
433 | |
434 | // old |
435 | var VT1 T1 |
436 | |
437 | // new |
438 | // i VT1: changed from T1 to int |
439 | // This fails because old T1 corresponds to both int and new T1. |
440 | var VT1 int |
441 | |
442 | // old |
443 | type t2 int |
444 | |
445 | var VT2 t2 |
446 | |
447 | // new |
448 | // OK: t2 corresponds to int. It's fine that old t2 |
449 | // doesn't exist in new. |
450 | var VT2 int |
451 | |
452 | // both |
453 | type t3 int |
454 | |
455 | func (t3) M() {} |
456 | |
457 | // old |
458 | var VT3 t3 |
459 | |
460 | // new |
461 | // i t3.M: removed |
462 | // Here the change from t3 to int is incompatible |
463 | // because old t3 has an exported method. |
464 | var VT3 int |
465 | |
466 | // old |
467 | var VT4 int |
468 | |
469 | // new |
470 | type t4 int |
471 | |
472 | // i VT4: changed from int to t4 |
473 | // This is incompatible because of code like |
474 | // VT4 + int(1) |
475 | // which works in old but fails in new. |
476 | // The difference from the above cases is that |
477 | // in those, we were merging two types into one; |
478 | // here, we are splitting int into t4 and int. |
479 | var VT4 t4 |
480 | |
481 | //////////////// Functions |
482 | |
483 | // old |
484 | func F1(a int, b string) map[u1]A { return nil } |
485 | func F2(int) {} |
486 | func F3(int) {} |
487 | func F4(int) int { return 0 } |
488 | func F5(int) int { return 0 } |
489 | func F6(int) {} |
490 | func F7(interface{}) {} |
491 | |
492 | // new |
493 | func F1(c int, d string) map[u2]AA { return nil } //OK: same (since u1 corresponds to u2) |
494 | |
495 | // i F2: changed from func(int) to func(int) bool |
496 | func F2(int) bool { return true } |
497 | |
498 | // i F3: changed from func(int) to func(int, int) |
499 | func F3(int, int) {} |
500 | |
501 | // i F4: changed from func(int) int to func(bool) int |
502 | func F4(bool) int { return 0 } |
503 | |
504 | // i F5: changed from func(int) int to func(int) string |
505 | func F5(int) string { return "" } |
506 | |
507 | // i F6: changed from func(int) to func(...int) |
508 | func F6(...int) {} |
509 | |
510 | // i F7: changed from func(interface{}) to func(interface{x()}) |
511 | func F7(a interface{ x() }) {} |
512 | |
513 | // old |
514 | func F8(bool) {} |
515 | |
516 | // new |
517 | // c F8: changed from func to var |
518 | var F8 func(bool) |
519 | |
520 | // old |
521 | var F9 func(int) |
522 | |
523 | // new |
524 | // i F9: changed from var to func |
525 | func F9(int) {} |
526 | |
527 | // both |
528 | // OK, even though new S1 is incompatible with old S1 (see below) |
529 | func F10(S1) {} |
530 | |
531 | //////////////// Structs |
532 | |
533 | // old |
534 | type S1 struct { |
535 | A int |
536 | B string |
537 | C bool |
538 | d float32 |
539 | } |
540 | |
541 | // new |
542 | type S1 = s1 |
543 | |
544 | type s1 struct { |
545 | C chan int |
546 | // i S1.C: changed from bool to chan int |
547 | A int |
548 | // i S1.B: removed |
549 | // i S1: old is comparable, new is not |
550 | x []int |
551 | d float32 |
552 | E bool |
553 | // c S1.E: added |
554 | } |
555 | |
556 | // old |
557 | type embed struct { |
558 | E string |
559 | } |
560 | |
561 | type S2 struct { |
562 | A int |
563 | embed |
564 | } |
565 | |
566 | // new |
567 | type embedx struct { |
568 | E string |
569 | } |
570 | |
571 | type S2 struct { |
572 | embedx // OK: the unexported embedded field changed names, but the exported field didn't |
573 | A int |
574 | } |
575 | |
576 | // both |
577 | type F int |
578 | |
579 | // old |
580 | type S3 struct { |
581 | A int |
582 | embed |
583 | } |
584 | |
585 | // new |
586 | type embed struct{ F int } |
587 | |
588 | type S3 struct { |
589 | // i S3.E: removed |
590 | embed |
591 | // c S3.F: added |
592 | A int |
593 | } |
594 | |
595 | // old |
596 | type embed2 struct { |
597 | embed3 |
598 | F // shadows embed3.F |
599 | } |
600 | |
601 | type embed3 struct { |
602 | F bool |
603 | } |
604 | |
605 | type alias = struct{ D bool } |
606 | |
607 | type S4 struct { |
608 | int |
609 | *embed2 |
610 | embed |
611 | E int // shadows embed.E |
612 | alias |
613 | A1 |
614 | *S4 |
615 | } |
616 | |
617 | // new |
618 | type S4 struct { |
619 | // OK: removed unexported fields |
620 | // D and F marked as added because they are now part of the immediate fields |
621 | D bool |
622 | // c S4.D: added |
623 | E int // OK: same as in old |
624 | F F |
625 | // c S4.F: added |
626 | A1 // OK: same |
627 | *S4 // OK: same (recursive embedding) |
628 | } |
629 | |
630 | //// Difference between exported selectable fields and exported immediate fields. |
631 | // both |
632 | type S5 struct{ A int } |
633 | |
634 | // old |
635 | // Exported immediate fields: A, S5 |
636 | // Exported selectable fields: A int, S5 S5 |
637 | type S6 struct { |
638 | S5 S5 |
639 | A int |
640 | } |
641 | |
642 | // new |
643 | // Exported immediate fields: S5 |
644 | // Exported selectable fields: A int, S5 S5. |
645 | |
646 | // i S6.A: removed |
647 | type S6 struct { |
648 | S5 |
649 | } |
650 | |
651 | //// Ambiguous fields can exist; they just can't be selected. |
652 | // both |
653 | type ( |
654 | embed7a struct{ E int } |
655 | embed7b struct{ E bool } |
656 | ) |
657 | |
658 | // old |
659 | type S7 struct { // legal, but no selectable fields |
660 | embed7a |
661 | embed7b |
662 | } |
663 | |
664 | // new |
665 | type S7 struct { |
666 | embed7a |
667 | embed7b |
668 | // c S7.E: added |
669 | E string |
670 | } |
671 | |
672 | //////////////// Method sets |
673 | |
674 | // old |
675 | type SM struct { |
676 | embedm |
677 | Embedm |
678 | } |
679 | |
680 | func (SM) V1() {} |
681 | func (SM) V2() {} |
682 | func (SM) V3() {} |
683 | func (SM) V4() {} |
684 | func (SM) v() {} |
685 | |
686 | func (*SM) P1() {} |
687 | func (*SM) P2() {} |
688 | func (*SM) P3() {} |
689 | func (*SM) P4() {} |
690 | func (*SM) p() {} |
691 | |
692 | type embedm int |
693 | |
694 | func (embedm) EV1() {} |
695 | func (embedm) EV2() {} |
696 | func (embedm) EV3() {} |
697 | func (*embedm) EP1() {} |
698 | func (*embedm) EP2() {} |
699 | func (*embedm) EP3() {} |
700 | |
701 | type Embedm struct { |
702 | A int |
703 | } |
704 | |
705 | func (Embedm) FV() {} |
706 | func (*Embedm) FP() {} |
707 | |
708 | type RepeatEmbedm struct { |
709 | Embedm |
710 | } |
711 | |
712 | // new |
713 | type SM struct { |
714 | embedm2 |
715 | embedm3 |
716 | Embedm |
717 | // i SM.A: changed from int to bool |
718 | } |
719 | |
720 | // c SMa: added |
721 | type SMa = SM |
722 | |
723 | func (SM) V1() {} // OK: same |
724 | |
725 | // func (SM) V2() {} |
726 | // i SM.V2: removed |
727 | |
728 | // i SM.V3: changed from func() to func(int) |
729 | func (SM) V3(int) {} |
730 | |
731 | // c SM.V5: added |
732 | func (SM) V5() {} |
733 | |
734 | func (SM) v(int) {} // OK: unexported method change |
735 | func (SM) v2() {} // OK: unexported method added |
736 | |
737 | func (*SM) P1() {} // OK: same |
738 | //func (*SM) P2() {} |
739 | // i (*SM).P2: removed |
740 | |
741 | // i (*SM).P3: changed from func() to func(int) |
742 | func (*SMa) P3(int) {} |
743 | |
744 | // c (*SM).P5: added |
745 | func (*SM) P5() {} |
746 | |
747 | // func (*SM) p() {} // OK: unexported method removed |
748 | |
749 | // Changing from a value to a pointer receiver or vice versa |
750 | // just looks like adding and removing a method. |
751 | |
752 | // i SM.V4: removed |
753 | // i (*SM).V4: changed from func() to func(int) |
754 | func (*SM) V4(int) {} |
755 | |
756 | // c SM.P4: added |
757 | // P4 is not removed from (*SM) because value methods |
758 | // are in the pointer method set. |
759 | func (SM) P4() {} |
760 | |
761 | type embedm2 int |
762 | |
763 | // i embedm.EV1: changed from func() to func(int) |
764 | func (embedm2) EV1(int) {} |
765 | |
766 | // i embedm.EV2, method set of SM: removed |
767 | // i embedm.EV2, method set of *SM: removed |
768 | |
769 | // i (*embedm).EP2, method set of *SM: removed |
770 | func (*embedm2) EP1() {} |
771 | |
772 | type embedm3 int |
773 | |
774 | func (embedm3) EV3() {} // OK: compatible with old embedm.EV3 |
775 | func (*embedm3) EP3() {} // OK: compatible with old (*embedm).EP3 |
776 | |
777 | type Embedm struct { |
778 | // i Embedm.A: changed from int to bool |
779 | A bool |
780 | } |
781 | |
782 | // i Embedm.FV: changed from func() to func(int) |
783 | func (Embedm) FV(int) {} |
784 | func (*Embedm) FP() {} |
785 | |
786 | type RepeatEmbedm struct { |
787 | // i RepeatEmbedm.A: changed from int to bool |
788 | Embedm |
789 | } |
790 | |
791 | //////////////// Whole-package interface satisfaction |
792 | |
793 | // old |
794 | type WI1 interface { |
795 | M1() |
796 | m1() |
797 | } |
798 | |
799 | type WI2 interface { |
800 | M2() |
801 | m2() |
802 | } |
803 | |
804 | type WS1 int |
805 | |
806 | func (WS1) M1() {} |
807 | func (WS1) m1() {} |
808 | |
809 | type WS2 int |
810 | |
811 | func (WS2) M2() {} |
812 | func (WS2) m2() {} |
813 | |
814 | // new |
815 | type WI1 interface { |
816 | M1() |
817 | m() |
818 | } |
819 | |
820 | type WS1 int |
821 | |
822 | func (WS1) M1() {} |
823 | |
824 | // i WS1: no longer implements WI1 |
825 | //func (WS1) m1() {} |
826 | |
827 | type WI2 interface { |
828 | M2() |
829 | m2() |
830 | // i WS2: no longer implements WI2 |
831 | m3() |
832 | } |
833 | |
834 | type WS2 int |
835 | |
836 | func (WS2) M2() {} |
837 | func (WS2) m2() {} |
838 | |
839 | //////////////// Miscellany |
840 | |
841 | // This verifies that the code works even through |
842 | // multiple levels of unexported typed. |
843 | |
844 | // old |
845 | var Z w |
846 | |
847 | type w []x |
848 | type x []z |
849 | type z int |
850 | |
851 | // new |
852 | var Z w |
853 | |
854 | type w []x |
855 | type x []z |
856 | |
857 | // i z: changed from int to bool |
858 | type z bool |
859 | |
860 | // old |
861 | type H struct{} |
862 | |
863 | func (H) M() {} |
864 | |
865 | // new |
866 | // i H: changed from struct{} to interface{M()} |
867 | type H interface { |
868 | M() |
869 | } |
870 | |
871 | //// Splitting types |
872 | |
873 | //// OK: in both old and new, {J1, K1, L1} name the same type. |
874 | // old |
875 | type ( |
876 | J1 = K1 |
877 | K1 = L1 |
878 | L1 int |
879 | ) |
880 | |
881 | // new |
882 | type ( |
883 | J1 = K1 |
884 | K1 int |
885 | L1 = J1 |
886 | ) |
887 | |
888 | //// Old has one type, K2; new has J2 and K2. |
889 | // both |
890 | type K2 int |
891 | |
892 | // old |
893 | type J2 = K2 |
894 | |
895 | // new |
896 | // i K2: changed from K2 to K2 |
897 | type J2 K2 // old K2 corresponds with new J2 |
898 | // old K2 also corresponds with new K2: problem |
899 | |
900 | // both |
901 | type k3 int |
902 | |
903 | var Vj3 j3 // expose j3 |
904 | |
905 | // old |
906 | type j3 = k3 |
907 | |
908 | // new |
909 | // OK: k3 isn't exposed |
910 | type j3 k3 |
911 | |
912 | // both |
913 | type k4 int |
914 | |
915 | var Vj4 j4 // expose j4 |
916 | var VK4 k4 // expose k4 |
917 | |
918 | // old |
919 | type j4 = k4 |
920 | |
921 | // new |
922 | // i Vj4: changed from k4 to j4 |
923 | // e.g. p.Vj4 = p.Vk4 |
924 | type j4 k4 |
925 |
Members