Skip to content

Commit 2d097e3

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: better error messages for copy built-in
Rather than relying on coreString, use the new commonUnder function to determine the argument slice element types. Factor out this functionality, which is shared for append and copy, into a new helper function sliceElem (similar to chanElem). Use sliceElem for both the append and copy implementation. As a result, the error messages for invalid copy calls are now more detailed. While at it, handle the special cases for append and copy first because they don't need the slice element computation. Finally, share the same type recording code for the special and general cases. As an aside, in commonUnder, be clearer in the code that the result is either a nil type and an error, or a non-nil type and a nil error. This matches in style what we do in sliceElem. Change-Id: I318bafc0d2d31df04f33b1b464ad50d581918671 Reviewed-on: https://go-review.googlesource.com/c/go/+/655675 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent e3ea8e6 commit 2d097e3

File tree

9 files changed

+238
-165
lines changed

9 files changed

+238
-165
lines changed

src/cmd/compile/internal/types2/builtins.go

Lines changed: 105 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -83,48 +83,19 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
8383
switch id {
8484
case _Append:
8585
// append(s S, x ...E) S, where E is the element type of S
86-
// spec: "The variadic function append appends zero or more values x to a slice s
87-
// of type S and returns the resulting slice, also of type S.
88-
// The values x are passed to a parameter of type ...E where E is the element type
89-
// of S and the respective parameter passing rules apply."
90-
S := x.typ
91-
92-
// determine E
93-
var E Type
94-
typeset(S, func(_, u Type) bool {
95-
s, _ := u.(*Slice)
96-
if s == nil {
97-
var cause string
98-
if x.isNil() {
99-
// Printing x in this case would just print "nil".
100-
// Special case this so we can emphasize "untyped".
101-
cause = "untyped nil"
102-
} else {
103-
cause = check.sprintf("%s", x)
104-
}
105-
check.errorf(x, InvalidAppend, "invalid append: first argument must be a slice; have %s", cause)
106-
E = nil
107-
return false
108-
}
109-
if E == nil {
110-
E = s.elem
111-
} else if !Identical(E, s.elem) {
112-
check.errorf(x, InvalidAppend, "invalid append: mismatched slice element types %s and %s in %s", E, s.elem, x)
113-
E = nil
114-
return false
115-
}
116-
return true
117-
})
118-
if E == nil {
119-
return
120-
}
121-
122-
// spec: "As a special case, append also accepts a first argument assignable
86+
// spec: "The variadic function append appends zero or more values x to
87+
// a slice s of type S and returns the resulting slice, also of type S.
88+
// The values x are passed to a parameter of type ...E where E is the
89+
// element type of S and the respective parameter passing rules apply.
90+
// As a special case, append also accepts a first argument assignable
12391
// to type []byte with a second argument of string type followed by ... .
12492
// This form appends the bytes of the string."
93+
94+
// get special case out of the way
95+
var sig *Signature
12596
if nargs == 2 && hasDots(call) {
12697
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
127-
y := args[1] // valid if != nil
98+
y := args[1]
12899
typeset(y.typ, func(_, u Type) bool {
129100
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
130101
return true
@@ -135,31 +106,35 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
135106
y = nil
136107
return false
137108
})
138-
139109
if y != nil {
140-
if check.recordTypes() {
141-
sig := makeSig(S, S, y.typ)
142-
sig.variadic = true
143-
check.recordBuiltinType(call.Fun, sig)
144-
}
145-
x.mode = value
146-
x.typ = S
147-
break
110+
// setting the signature also signals that we're done
111+
sig = makeSig(x.typ, x.typ, y.typ)
112+
sig.variadic = true
148113
}
149114
}
150115
}
151116

152-
// check general case by creating custom signature
153-
sig := makeSig(S, S, NewSlice(E)) // []E required for variadic signature
154-
sig.variadic = true
155-
check.arguments(call, sig, nil, nil, args, nil) // discard result (we know the result type)
156-
// ok to continue even if check.arguments reported errors
117+
// general case
118+
if sig == nil {
119+
// spec: "If S is a type parameter, all types in its type set
120+
// must have the same underlying slice type []E."
121+
E, err := sliceElem(x)
122+
if err != nil {
123+
check.errorf(x, InvalidAppend, "invalid append: %s", err.format(check))
124+
return
125+
}
126+
// check arguments by creating custom signature
127+
sig = makeSig(x.typ, x.typ, NewSlice(E)) // []E required for variadic signature
128+
sig.variadic = true
129+
check.arguments(call, sig, nil, nil, args, nil) // discard result (we know the result type)
130+
// ok to continue even if check.arguments reported errors
131+
}
157132

158-
x.mode = value
159-
x.typ = S
160133
if check.recordTypes() {
161134
check.recordBuiltinType(call.Fun, sig)
162135
}
136+
x.mode = value
137+
// x.typ is unchanged
163138

164139
case _Cap, _Len:
165140
// cap(x)
@@ -376,25 +351,54 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
376351
x.typ = resTyp
377352

378353
case _Copy:
379-
// copy(x, y []T) int
380-
u, _ := commonUnder(x.typ, nil)
381-
dst, _ := u.(*Slice)
382-
354+
// copy(x, y []E) int
355+
// spec: "The function copy copies slice elements from a source src to a destination
356+
// dst and returns the number of elements copied. Both arguments must have identical
357+
// element type E and must be assignable to a slice of type []E.
358+
// The number of elements copied is the minimum of len(src) and len(dst).
359+
// As a special case, copy also accepts a destination argument assignable to type
360+
// []byte with a source argument of a string type.
361+
// This form copies the bytes from the string into the byte slice."
362+
363+
// get special case out of the way
383364
y := args[1]
384-
src0 := coreString(y.typ)
385-
if src0 != nil && isString(src0) {
386-
src0 = NewSlice(universeByte)
387-
}
388-
src, _ := src0.(*Slice)
389-
390-
if dst == nil || src == nil {
391-
check.errorf(x, InvalidCopy, invalidArg+"copy expects slice arguments; found %s and %s", x, y)
392-
return
365+
var special bool
366+
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
367+
special = true
368+
typeset(y.typ, func(_, u Type) bool {
369+
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
370+
return true
371+
}
372+
if isString(u) {
373+
return true
374+
}
375+
special = false
376+
return false
377+
})
393378
}
394379

395-
if !Identical(dst.elem, src.elem) {
396-
check.errorf(x, InvalidCopy, invalidArg+"arguments to copy %s and %s have different element types %s and %s", x, y, dst.elem, src.elem)
397-
return
380+
// general case
381+
if !special {
382+
// spec: "If the type of one or both arguments is a type parameter, all types
383+
// in their respective type sets must have the same underlying slice type []E."
384+
dstE, err := sliceElem(x)
385+
if err != nil {
386+
check.errorf(x, InvalidCopy, "invalid copy: %s", err.format(check))
387+
return
388+
}
389+
srcE, err := sliceElem(y)
390+
if err != nil {
391+
// If we have a string, for a better error message proceed with byte element type.
392+
if !allString(y.typ) {
393+
check.errorf(y, InvalidCopy, "invalid copy: %s", err.format(check))
394+
return
395+
}
396+
srcE = universeByte
397+
}
398+
if !Identical(dstE, srcE) {
399+
check.errorf(x, InvalidCopy, "invalid copy: arguments %s and %s have different element types %s and %s", x, y, dstE, srcE)
400+
return
401+
}
398402
}
399403

400404
if check.recordTypes() {
@@ -938,6 +942,37 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
938942
return true
939943
}
940944

945+
// sliceElem returns the slice element type for a slice operand x
946+
// or a type error if x is not a slice (or a type set of slices).
947+
func sliceElem(x *operand) (Type, *typeError) {
948+
var E Type
949+
var err *typeError
950+
typeset(x.typ, func(_, u Type) bool {
951+
s, _ := u.(*Slice)
952+
if s == nil {
953+
if x.isNil() {
954+
// Printing x in this case would just print "nil".
955+
// Special case this so we can emphasize "untyped".
956+
err = typeErrorf("argument must be a slice; have untyped nil")
957+
} else {
958+
err = typeErrorf("argument must be a slice; have %s", x)
959+
}
960+
return false
961+
}
962+
if E == nil {
963+
E = s.elem
964+
} else if !Identical(E, s.elem) {
965+
err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
966+
return false
967+
}
968+
return true
969+
})
970+
if err != nil {
971+
return nil, err
972+
}
973+
return E, nil
974+
}
975+
941976
// hasVarSize reports if the size of type t is variable due to type parameters
942977
// or if the type is infinitely-sized due to a cycle for which the type has not
943978
// yet been checked.

src/cmd/compile/internal/types2/under.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,13 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
8383
var err *typeError
8484

8585
bad := func(format string, args ...any) bool {
86-
cu = nil
8786
err = typeErrorf(format, args...)
8887
return false
8988
}
9089

9190
typeset(t, func(t, u Type) bool {
9291
if cond != nil {
9392
if err = cond(t, u); err != nil {
94-
cu = nil
9593
return false
9694
}
9795
}
@@ -132,7 +130,10 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
132130
return true
133131
})
134132

135-
return cu, err
133+
if err != nil {
134+
return nil, err
135+
}
136+
return cu, nil
136137
}
137138

138139
// coreString is like coreType but also considers []byte

0 commit comments

Comments
 (0)