whatcanGOwrong
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
package exported_fields
|
||||
|
||||
// Used for testing exportedFields.
|
||||
// Its exported fields are:
|
||||
//
|
||||
// A1 [1]int
|
||||
// D bool
|
||||
// E int
|
||||
// F F
|
||||
// S *S
|
||||
type (
|
||||
S struct {
|
||||
int
|
||||
*embed2
|
||||
embed
|
||||
E int // shadows embed.E
|
||||
alias
|
||||
A1
|
||||
*S
|
||||
}
|
||||
|
||||
A1 [1]int
|
||||
|
||||
embed struct {
|
||||
E string
|
||||
}
|
||||
|
||||
embed2 struct {
|
||||
embed3
|
||||
F // shadows embed3.F
|
||||
}
|
||||
embed3 struct {
|
||||
F bool
|
||||
}
|
||||
alias = struct{ D bool }
|
||||
|
||||
F int
|
||||
)
|
||||
+969
@@ -0,0 +1,969 @@
|
||||
// This file is split into two packages, old and new.
|
||||
// It is syntactically valid Go so that gofmt can process it.
|
||||
//
|
||||
// If a comment begins with: Then:
|
||||
// old write subsequent lines to the "old" package
|
||||
// new write subsequent lines to the "new" package
|
||||
// both write subsequent lines to both packages
|
||||
// c expect a compatible error with the following text
|
||||
// i expect an incompatible error with the following text
|
||||
package ignore
|
||||
|
||||
// both
|
||||
import "io"
|
||||
|
||||
//////////////// Basics
|
||||
|
||||
// Same type in both: OK.
|
||||
// both
|
||||
type A int
|
||||
|
||||
// Changing the type is an incompatible change.
|
||||
// old
|
||||
type B int
|
||||
|
||||
// new
|
||||
// i B: changed from int to string
|
||||
type B string
|
||||
|
||||
// Adding a new type, whether alias or not, is a compatible change.
|
||||
// new
|
||||
// c AA: added
|
||||
type AA = A
|
||||
|
||||
// c B1: added
|
||||
type B1 bool
|
||||
|
||||
// Change of type for an unexported name doesn't matter...
|
||||
// old
|
||||
type t int
|
||||
|
||||
// new
|
||||
type t string // OK: t isn't part of the API
|
||||
|
||||
// ...unless it is exposed.
|
||||
// both
|
||||
var V2 u
|
||||
|
||||
// old
|
||||
type u string
|
||||
|
||||
// new
|
||||
// i u: changed from string to int
|
||||
type u int
|
||||
|
||||
// An exposed, unexported type can be renamed.
|
||||
// both
|
||||
type u2 int
|
||||
|
||||
// old
|
||||
type u1 int
|
||||
|
||||
var V5 u1
|
||||
|
||||
// new
|
||||
var V5 u2 // OK: V5 has changed type, but old u1 corresopnds to new u2
|
||||
|
||||
// Splitting a single type into two is an incompatible change.
|
||||
// both
|
||||
type u3 int
|
||||
|
||||
// old
|
||||
type (
|
||||
Split1 = u1
|
||||
Split2 = u1
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
Split1 = u2 // OK, since old u1 corresponds to new u2
|
||||
|
||||
// This tries to make u1 correspond to u3
|
||||
// i Split2: changed from u1 to u3
|
||||
Split2 = u3
|
||||
)
|
||||
|
||||
// Merging two types into one is OK.
|
||||
// old
|
||||
type (
|
||||
GoodMerge1 = u2
|
||||
GoodMerge2 = u3
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
GoodMerge1 = u3
|
||||
GoodMerge2 = u3
|
||||
)
|
||||
|
||||
// Merging isn't OK here because a method is lost.
|
||||
// both
|
||||
type u4 int
|
||||
|
||||
func (u4) M() {}
|
||||
|
||||
// old
|
||||
type (
|
||||
BadMerge1 = u3
|
||||
BadMerge2 = u4
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
BadMerge1 = u3
|
||||
// i u4.M: removed
|
||||
// What's really happening here is that old u4 corresponds to new u3,
|
||||
// and new u3's method set is not a superset of old u4's.
|
||||
BadMerge2 = u3
|
||||
)
|
||||
|
||||
// old
|
||||
type Rem int
|
||||
|
||||
// new
|
||||
// i Rem: removed
|
||||
|
||||
//////////////// Constants
|
||||
|
||||
// type changes
|
||||
// old
|
||||
const (
|
||||
C1 = 1
|
||||
C2 int = 2
|
||||
C3 = 3
|
||||
C4 u1 = 4
|
||||
)
|
||||
|
||||
var V8 int
|
||||
|
||||
// new
|
||||
const (
|
||||
// i C1: changed from untyped int to untyped string
|
||||
C1 = "1"
|
||||
// i C2: changed from int to untyped int
|
||||
C2 = -1
|
||||
// i C3: changed from untyped int to int
|
||||
C3 int = 3
|
||||
// i V8: changed from var to const
|
||||
V8 int = 1
|
||||
C4 u2 = 4 // OK: u1 corresponds to u2
|
||||
)
|
||||
|
||||
// value change
|
||||
// old
|
||||
const (
|
||||
Cr1 = 1
|
||||
Cr2 = "2"
|
||||
Cr3 = 3.5
|
||||
Cr4 = complex(0, 4.1)
|
||||
)
|
||||
|
||||
// new
|
||||
const (
|
||||
// i Cr1: value changed from 1 to -1
|
||||
Cr1 = -1
|
||||
// i Cr2: value changed from "2" to "3"
|
||||
Cr2 = "3"
|
||||
// i Cr3: value changed from 3.5 to 3.8
|
||||
Cr3 = 3.8
|
||||
// i Cr4: value changed from (0 + 4.1i) to (4.1 + 0i)
|
||||
Cr4 = complex(4.1, 0)
|
||||
)
|
||||
|
||||
//////////////// Variables
|
||||
|
||||
// simple type changes
|
||||
// old
|
||||
var (
|
||||
V1 string
|
||||
V3 A
|
||||
V7 <-chan int
|
||||
)
|
||||
|
||||
// new
|
||||
var (
|
||||
// i V1: changed from string to []string
|
||||
V1 []string
|
||||
V3 A // OK: same
|
||||
// i V7: changed from <-chan int to chan int
|
||||
V7 chan int
|
||||
)
|
||||
|
||||
// interface type changes
|
||||
// old
|
||||
var (
|
||||
V9 interface{ M() }
|
||||
V10 interface{ M() }
|
||||
V11 interface{ M() }
|
||||
)
|
||||
|
||||
// new
|
||||
var (
|
||||
// i V9: changed from interface{M()} to interface{}
|
||||
V9 interface{}
|
||||
// i V10: changed from interface{M()} to interface{M(); M2()}
|
||||
V10 interface {
|
||||
M2()
|
||||
M()
|
||||
}
|
||||
// i V11: changed from interface{M()} to interface{M(int)}
|
||||
V11 interface{ M(int) }
|
||||
)
|
||||
|
||||
// struct type changes
|
||||
// old
|
||||
var (
|
||||
VS1 struct{ A, B int }
|
||||
VS2 struct{ A, B int }
|
||||
VS3 struct{ A, B int }
|
||||
VS4 struct {
|
||||
A int
|
||||
u1
|
||||
}
|
||||
)
|
||||
|
||||
// new
|
||||
var (
|
||||
// i VS1: changed from struct{A int; B int} to struct{B int; A int}
|
||||
VS1 struct{ B, A int }
|
||||
// i VS2: changed from struct{A int; B int} to struct{A int}
|
||||
VS2 struct{ A int }
|
||||
// i VS3: changed from struct{A int; B int} to struct{A int; B int; C int}
|
||||
VS3 struct{ A, B, C int }
|
||||
VS4 struct {
|
||||
A int
|
||||
u2
|
||||
}
|
||||
)
|
||||
|
||||
//////////////// Types
|
||||
|
||||
// old
|
||||
const C5 = 3
|
||||
|
||||
type (
|
||||
A1 [1]int
|
||||
A2 [2]int
|
||||
A3 [C5]int
|
||||
)
|
||||
|
||||
// new
|
||||
// i C5: value changed from 3 to 4
|
||||
const C5 = 4
|
||||
|
||||
type (
|
||||
A1 [1]int
|
||||
// i A2: changed from [2]int to [2]bool
|
||||
A2 [2]bool
|
||||
// i A3: changed from [3]int to [4]int
|
||||
A3 [C5]int
|
||||
)
|
||||
|
||||
// old
|
||||
type (
|
||||
Sl []int
|
||||
P1 *int
|
||||
P2 *u1
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
// i Sl: changed from []int to []string
|
||||
Sl []string
|
||||
// i P1: changed from *int to **bool
|
||||
P1 **bool
|
||||
P2 *u2 // OK: u1 corresponds to u2
|
||||
)
|
||||
|
||||
// old
|
||||
type Bc1 int32
|
||||
type Bc2 uint
|
||||
type Bc3 float32
|
||||
type Bc4 complex64
|
||||
|
||||
// new
|
||||
// c Bc1: changed from int32 to int
|
||||
type Bc1 int
|
||||
|
||||
// c Bc2: changed from uint to uint64
|
||||
type Bc2 uint64
|
||||
|
||||
// c Bc3: changed from float32 to float64
|
||||
type Bc3 float64
|
||||
|
||||
// c Bc4: changed from complex64 to complex128
|
||||
type Bc4 complex128
|
||||
|
||||
// old
|
||||
type Bi1 int32
|
||||
type Bi2 uint
|
||||
type Bi3 float64
|
||||
type Bi4 complex128
|
||||
|
||||
// new
|
||||
// i Bi1: changed from int32 to int16
|
||||
type Bi1 int16
|
||||
|
||||
// i Bi2: changed from uint to uint32
|
||||
type Bi2 uint32
|
||||
|
||||
// i Bi3: changed from float64 to float32
|
||||
type Bi3 float32
|
||||
|
||||
// i Bi4: changed from complex128 to complex64
|
||||
type Bi4 complex64
|
||||
|
||||
// old
|
||||
type (
|
||||
M1 map[string]int
|
||||
M2 map[string]int
|
||||
M3 map[string]int
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
M1 map[string]int
|
||||
// i M2: changed from map[string]int to map[int]int
|
||||
M2 map[int]int
|
||||
// i M3: changed from map[string]int to map[string]string
|
||||
M3 map[string]string
|
||||
)
|
||||
|
||||
// old
|
||||
type (
|
||||
Ch1 chan int
|
||||
Ch2 <-chan int
|
||||
Ch3 chan int
|
||||
Ch4 <-chan int
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
// i Ch1, element type: changed from int to bool
|
||||
Ch1 chan bool
|
||||
// i Ch2: changed direction
|
||||
Ch2 chan<- int
|
||||
// i Ch3: changed direction
|
||||
Ch3 <-chan int
|
||||
// c Ch4: removed direction
|
||||
Ch4 chan int
|
||||
)
|
||||
|
||||
// old
|
||||
type I1 interface {
|
||||
M1()
|
||||
M2()
|
||||
}
|
||||
|
||||
// new
|
||||
type I1 interface {
|
||||
// M1()
|
||||
// i I1.M1: removed
|
||||
M2(int)
|
||||
// i I1.M2: changed from func() to func(int)
|
||||
M3()
|
||||
// i I1.M3: added
|
||||
m()
|
||||
// i I1.m: added unexported method
|
||||
}
|
||||
|
||||
// old
|
||||
type I2 interface {
|
||||
M1()
|
||||
m()
|
||||
}
|
||||
|
||||
// new
|
||||
type I2 interface {
|
||||
M1()
|
||||
// m() Removing an unexported method is OK.
|
||||
m2() // OK, because old already had an unexported method
|
||||
// c I2.M2: added
|
||||
M2()
|
||||
}
|
||||
|
||||
// old
|
||||
type I3 interface {
|
||||
io.Reader
|
||||
M()
|
||||
}
|
||||
|
||||
// new
|
||||
// OK: what matters is the method set; the name of the embedded
|
||||
// interface isn't important.
|
||||
type I3 interface {
|
||||
M()
|
||||
Read([]byte) (int, error)
|
||||
}
|
||||
|
||||
// old
|
||||
type I4 io.Writer
|
||||
|
||||
// new
|
||||
// OK: in both, I4 is a distinct type from io.Writer, and
|
||||
// the old and new I4s have the same method set.
|
||||
type I4 interface {
|
||||
Write([]byte) (int, error)
|
||||
}
|
||||
|
||||
// old
|
||||
type I5 = io.Writer
|
||||
|
||||
// new
|
||||
// i I5: changed from io.Writer to I5
|
||||
// In old, I5 and io.Writer are the same type; in new,
|
||||
// they are different. That can break something like:
|
||||
//
|
||||
// var _ func(io.Writer) = func(pkg.I6) {}
|
||||
type I5 io.Writer
|
||||
|
||||
// old
|
||||
type I6 interface{ Write([]byte) (int, error) }
|
||||
|
||||
// new
|
||||
// i I6: changed from I6 to io.Writer
|
||||
// Similar to the above.
|
||||
type I6 = io.Writer
|
||||
|
||||
//// correspondence with a basic type
|
||||
// Basic types are technically defined types, but they aren't
|
||||
// represented that way in go/types, so the cases below are special.
|
||||
|
||||
// both
|
||||
type T1 int
|
||||
|
||||
// old
|
||||
var VT1 T1
|
||||
|
||||
// new
|
||||
// i VT1: changed from T1 to int
|
||||
// This fails because old T1 corresponds to both int and new T1.
|
||||
var VT1 int
|
||||
|
||||
// old
|
||||
type t2 int
|
||||
|
||||
var VT2 t2
|
||||
|
||||
// new
|
||||
// OK: t2 corresponds to int. It's fine that old t2
|
||||
// doesn't exist in new.
|
||||
var VT2 int
|
||||
|
||||
// both
|
||||
type t3 int
|
||||
|
||||
func (t3) M() {}
|
||||
|
||||
// old
|
||||
var VT3 t3
|
||||
|
||||
// new
|
||||
// i t3.M: removed
|
||||
// Here the change from t3 to int is incompatible
|
||||
// because old t3 has an exported method.
|
||||
var VT3 int
|
||||
|
||||
// old
|
||||
var VT4 int
|
||||
|
||||
// new
|
||||
type t4 int
|
||||
|
||||
// i VT4: changed from int to t4
|
||||
// This is incompatible because of code like
|
||||
//
|
||||
// VT4 + int(1)
|
||||
//
|
||||
// which works in old but fails in new.
|
||||
// The difference from the above cases is that
|
||||
// in those, we were merging two types into one;
|
||||
// here, we are splitting int into t4 and int.
|
||||
var VT4 t4
|
||||
|
||||
//////////////// Functions
|
||||
|
||||
// old
|
||||
func F1(a int, b string) map[u1]A { return nil }
|
||||
func F2(int) {}
|
||||
func F3(int) {}
|
||||
func F4(int) int { return 0 }
|
||||
func F5(int) int { return 0 }
|
||||
func F6(int) {}
|
||||
func F7(interface{}) {}
|
||||
|
||||
// new
|
||||
func F1(c int, d string) map[u2]AA { return nil } //OK: same (since u1 corresponds to u2)
|
||||
|
||||
// i F2: changed from func(int) to func(int) bool
|
||||
func F2(int) bool { return true }
|
||||
|
||||
// i F3: changed from func(int) to func(int, int)
|
||||
func F3(int, int) {}
|
||||
|
||||
// i F4: changed from func(int) int to func(bool) int
|
||||
func F4(bool) int { return 0 }
|
||||
|
||||
// i F5: changed from func(int) int to func(int) string
|
||||
func F5(int) string { return "" }
|
||||
|
||||
// i F6: changed from func(int) to func(...int)
|
||||
func F6(...int) {}
|
||||
|
||||
// i F7: changed from func(interface{}) to func(interface{x()})
|
||||
func F7(a interface{ x() }) {}
|
||||
|
||||
// old
|
||||
func F8(bool) {}
|
||||
|
||||
// new
|
||||
// c F8: changed from func to var
|
||||
var F8 func(bool)
|
||||
|
||||
// old
|
||||
var F9 func(int)
|
||||
|
||||
// new
|
||||
// i F9: changed from var to func
|
||||
func F9(int) {}
|
||||
|
||||
// both
|
||||
// OK, even though new S1 is incompatible with old S1 (see below)
|
||||
func F10(S1) {}
|
||||
|
||||
//////////////// Structs
|
||||
|
||||
// old
|
||||
type S1 struct {
|
||||
A int
|
||||
B string
|
||||
C bool
|
||||
d float32
|
||||
}
|
||||
|
||||
// new
|
||||
type S1 = s1
|
||||
|
||||
type s1 struct {
|
||||
C chan int
|
||||
// i S1.C: changed from bool to chan int
|
||||
A int
|
||||
// i S1.B: removed
|
||||
// i S1: old is comparable, new is not
|
||||
x []int
|
||||
d float32
|
||||
E bool
|
||||
// c S1.E: added
|
||||
}
|
||||
|
||||
// old
|
||||
type embed struct {
|
||||
E string
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
A int
|
||||
embed
|
||||
}
|
||||
|
||||
// new
|
||||
type embedx struct {
|
||||
E string
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
embedx // OK: the unexported embedded field changed names, but the exported field didn't
|
||||
A int
|
||||
}
|
||||
|
||||
// both
|
||||
type F int
|
||||
|
||||
// old
|
||||
type S3 struct {
|
||||
A int
|
||||
embed
|
||||
}
|
||||
|
||||
// new
|
||||
type embed struct{ F int }
|
||||
|
||||
type S3 struct {
|
||||
// i S3.E: removed
|
||||
embed
|
||||
// c S3.F: added
|
||||
A int
|
||||
}
|
||||
|
||||
// old
|
||||
type embed2 struct {
|
||||
embed3
|
||||
F // shadows embed3.F
|
||||
}
|
||||
|
||||
type embed3 struct {
|
||||
F bool
|
||||
}
|
||||
|
||||
type alias = struct{ D bool }
|
||||
|
||||
type S4 struct {
|
||||
int
|
||||
*embed2
|
||||
embed
|
||||
E int // shadows embed.E
|
||||
alias
|
||||
A1
|
||||
*S4
|
||||
}
|
||||
|
||||
// new
|
||||
type S4 struct {
|
||||
// OK: removed unexported fields
|
||||
// D and F marked as added because they are now part of the immediate fields
|
||||
D bool
|
||||
// c S4.D: added
|
||||
E int // OK: same as in old
|
||||
F F
|
||||
// c S4.F: added
|
||||
A1 // OK: same
|
||||
*S4 // OK: same (recursive embedding)
|
||||
}
|
||||
|
||||
// Difference between exported selectable fields and exported immediate fields.
|
||||
// both
|
||||
type S5 struct{ A int }
|
||||
|
||||
// old
|
||||
// Exported immediate fields: A, S5
|
||||
// Exported selectable fields: A int, S5 S5
|
||||
type S6 struct {
|
||||
S5 S5
|
||||
A int
|
||||
}
|
||||
|
||||
// new
|
||||
// Exported immediate fields: S5
|
||||
// Exported selectable fields: A int, S5 S5.
|
||||
|
||||
// i S6.A: removed
|
||||
type S6 struct {
|
||||
S5
|
||||
}
|
||||
|
||||
// Ambiguous fields can exist; they just can't be selected.
|
||||
// both
|
||||
type (
|
||||
embed7a struct{ E int }
|
||||
embed7b struct{ E bool }
|
||||
)
|
||||
|
||||
// old
|
||||
type S7 struct { // legal, but no selectable fields
|
||||
embed7a
|
||||
embed7b
|
||||
}
|
||||
|
||||
// new
|
||||
type S7 struct {
|
||||
embed7a
|
||||
embed7b
|
||||
// c S7.E: added
|
||||
E string
|
||||
}
|
||||
|
||||
//// Method sets
|
||||
|
||||
// old
|
||||
type SM struct {
|
||||
embedm
|
||||
Embedm
|
||||
}
|
||||
|
||||
func (SM) V1() {}
|
||||
func (SM) V2() {}
|
||||
func (SM) V3() {}
|
||||
func (SM) V4() {}
|
||||
func (SM) v() {}
|
||||
|
||||
func (*SM) P1() {}
|
||||
func (*SM) P2() {}
|
||||
func (*SM) P3() {}
|
||||
func (*SM) P4() {}
|
||||
func (*SM) p() {}
|
||||
|
||||
type embedm int
|
||||
|
||||
func (embedm) EV1() {}
|
||||
func (embedm) EV2() {}
|
||||
func (embedm) EV3() {}
|
||||
func (*embedm) EP1() {}
|
||||
func (*embedm) EP2() {}
|
||||
func (*embedm) EP3() {}
|
||||
|
||||
type Embedm struct {
|
||||
A int
|
||||
}
|
||||
|
||||
func (Embedm) FV() {}
|
||||
func (*Embedm) FP() {}
|
||||
|
||||
type RepeatEmbedm struct {
|
||||
Embedm
|
||||
}
|
||||
|
||||
// new
|
||||
type SM struct {
|
||||
embedm2
|
||||
embedm3
|
||||
Embedm
|
||||
// i SM.A: changed from int to bool
|
||||
}
|
||||
|
||||
// c SMa: added
|
||||
type SMa = SM
|
||||
|
||||
func (SM) V1() {} // OK: same
|
||||
|
||||
// func (SM) V2() {}
|
||||
// i SM.V2: removed
|
||||
|
||||
// i SM.V3: changed from func() to func(int)
|
||||
func (SM) V3(int) {}
|
||||
|
||||
// c SM.V5: added
|
||||
func (SM) V5() {}
|
||||
|
||||
func (SM) v(int) {} // OK: unexported method change
|
||||
func (SM) v2() {} // OK: unexported method added
|
||||
|
||||
func (*SM) P1() {} // OK: same
|
||||
//func (*SM) P2() {}
|
||||
// i (*SM).P2: removed
|
||||
|
||||
// i (*SM).P3: changed from func() to func(int)
|
||||
func (*SMa) P3(int) {}
|
||||
|
||||
// c (*SM).P5: added
|
||||
func (*SM) P5() {}
|
||||
|
||||
// func (*SM) p() {} // OK: unexported method removed
|
||||
|
||||
// Changing from a value to a pointer receiver or vice versa
|
||||
// just looks like adding and removing a method.
|
||||
|
||||
// i SM.V4: removed
|
||||
// i (*SM).V4: changed from func() to func(int)
|
||||
func (*SM) V4(int) {}
|
||||
|
||||
// c SM.P4: added
|
||||
// P4 is not removed from (*SM) because value methods
|
||||
// are in the pointer method set.
|
||||
func (SM) P4() {}
|
||||
|
||||
type embedm2 int
|
||||
|
||||
// i embedm.EV1: changed from func() to func(int)
|
||||
func (embedm2) EV1(int) {}
|
||||
|
||||
// i embedm.EV2, method set of SM: removed
|
||||
// i embedm.EV2, method set of *SM: removed
|
||||
|
||||
// i (*embedm).EP2, method set of *SM: removed
|
||||
func (*embedm2) EP1() {}
|
||||
|
||||
type embedm3 int
|
||||
|
||||
func (embedm3) EV3() {} // OK: compatible with old embedm.EV3
|
||||
func (*embedm3) EP3() {} // OK: compatible with old (*embedm).EP3
|
||||
|
||||
type Embedm struct {
|
||||
// i Embedm.A: changed from int to bool
|
||||
A bool
|
||||
}
|
||||
|
||||
// i Embedm.FV: changed from func() to func(int)
|
||||
func (Embedm) FV(int) {}
|
||||
func (*Embedm) FP() {}
|
||||
|
||||
type RepeatEmbedm struct {
|
||||
// i RepeatEmbedm.A: changed from int to bool
|
||||
Embedm
|
||||
}
|
||||
|
||||
//////////////// Whole-package interface satisfaction
|
||||
|
||||
// old
|
||||
type WI1 interface {
|
||||
M1()
|
||||
m1()
|
||||
}
|
||||
|
||||
type WI2 interface {
|
||||
M2()
|
||||
m2()
|
||||
}
|
||||
|
||||
type WS1 int
|
||||
|
||||
func (WS1) M1() {}
|
||||
func (WS1) m1() {}
|
||||
|
||||
type WS2 int
|
||||
|
||||
func (WS2) M2() {}
|
||||
func (WS2) m2() {}
|
||||
|
||||
// new
|
||||
type WI1 interface {
|
||||
M1()
|
||||
m()
|
||||
}
|
||||
|
||||
type WS1 int
|
||||
|
||||
func (WS1) M1() {}
|
||||
|
||||
// i WS1: no longer implements WI1
|
||||
//func (WS1) m1() {}
|
||||
|
||||
type WI2 interface {
|
||||
M2()
|
||||
m2()
|
||||
// i WS2: no longer implements WI2
|
||||
m3()
|
||||
}
|
||||
|
||||
type WS2 int
|
||||
|
||||
func (WS2) M2() {}
|
||||
func (WS2) m2() {}
|
||||
|
||||
//////////////// Miscellany
|
||||
|
||||
// This verifies that the code works even through
|
||||
// multiple levels of unexported typed.
|
||||
|
||||
// old
|
||||
var Z w
|
||||
|
||||
type w []x
|
||||
type x []z
|
||||
type z int
|
||||
|
||||
// new
|
||||
var Z w
|
||||
|
||||
type w []x
|
||||
type x []z
|
||||
|
||||
// i z: changed from int to bool
|
||||
type z bool
|
||||
|
||||
// old
|
||||
type H struct{}
|
||||
|
||||
func (H) M() {}
|
||||
|
||||
// new
|
||||
// i H: changed from struct{} to interface{M()}
|
||||
type H interface {
|
||||
M()
|
||||
}
|
||||
|
||||
//// Splitting types
|
||||
|
||||
// OK: in both old and new, {J1, K1, L1} name the same type.
|
||||
// old
|
||||
type (
|
||||
J1 = K1
|
||||
K1 = L1
|
||||
L1 int
|
||||
)
|
||||
|
||||
// new
|
||||
type (
|
||||
J1 = K1
|
||||
K1 int
|
||||
L1 = J1
|
||||
)
|
||||
|
||||
// Old has one type, K2; new has J2 and K2.
|
||||
// both
|
||||
type K2 int
|
||||
|
||||
// old
|
||||
type J2 = K2
|
||||
|
||||
// new
|
||||
// i K2: changed from K2 to K2
|
||||
type J2 K2 // old K2 corresponds with new J2
|
||||
// old K2 also corresponds with new K2: problem
|
||||
|
||||
// both
|
||||
type k3 int
|
||||
|
||||
var Vj3 j3 // expose j3
|
||||
|
||||
// old
|
||||
type j3 = k3
|
||||
|
||||
// new
|
||||
// OK: k3 isn't exposed
|
||||
type j3 k3
|
||||
|
||||
// both
|
||||
type k4 int
|
||||
|
||||
var Vj4 j4 // expose j4
|
||||
var VK4 k4 // expose k4
|
||||
|
||||
// old
|
||||
type j4 = k4
|
||||
|
||||
// new
|
||||
// i Vj4: changed from k4 to j4
|
||||
// e.g. p.Vj4 = p.Vk4
|
||||
type j4 k4
|
||||
|
||||
//// Generics
|
||||
|
||||
// old
|
||||
type G[T any] []T
|
||||
|
||||
// new
|
||||
// OK: param name change
|
||||
type G[A any] []A
|
||||
|
||||
// old
|
||||
type GM[A, B comparable] map[A]B
|
||||
|
||||
// new
|
||||
// i GM: changed from map[A]B to map[B]A
|
||||
type GM[A, B comparable] map[B]A
|
||||
|
||||
// old
|
||||
type GT[V any] struct {
|
||||
}
|
||||
|
||||
func (GT[V]) M(*GT[V]) {}
|
||||
|
||||
// new
|
||||
// OK
|
||||
type GT[V any] struct {
|
||||
}
|
||||
|
||||
func (GT[V]) M(*GT[V]) {}
|
||||
|
||||
// old
|
||||
type GT2[V any] struct {
|
||||
}
|
||||
|
||||
func (GT2[V]) M(*GT2[V]) {}
|
||||
|
||||
// new
|
||||
// i GT2: changed from GT2[V any] to GT2[V comparable]
|
||||
type GT2[V comparable] struct {
|
||||
}
|
||||
|
||||
func (GT2[V]) M(*GT2[V]) {}
|
||||
Reference in New Issue
Block a user