mirror of https://github.com/golang/go.git
go/types, types2: better error messages for for-range clauses
Provide the exact error cause instead of reporting a missing core type. For #70128. Change-Id: I835698fa1f22382711bd54b974d2c87ee17e9065 Reviewed-on: https://go-review.googlesource.com/c/go/+/651215 Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Robert Griesemer <gri@google.com> TryBot-Bypass: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
bdcd6d1b65
commit
d45d502fbb
|
|
@ -32,7 +32,7 @@ func CoreType(t Type) Type {
|
|||
// RangeKeyVal returns the key and value types for a range over typ.
|
||||
// It panics if range over typ is invalid.
|
||||
func RangeKeyVal(typ Type) (Type, Type) {
|
||||
key, val, _, ok := rangeKeyVal(typ, nil)
|
||||
key, val, _, ok := rangeKeyVal(nil, typ, nil)
|
||||
assert(ok)
|
||||
return key, val
|
||||
}
|
||||
|
|
|
|||
|
|
@ -859,8 +859,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
// determine key/value types
|
||||
var key, val Type
|
||||
if x.mode != invalid {
|
||||
// Ranging over a type parameter is permitted if it has a core type.
|
||||
k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
|
||||
k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
|
||||
return check.allowVersion(v)
|
||||
})
|
||||
switch {
|
||||
|
|
@ -992,19 +991,23 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
}
|
||||
|
||||
// rangeKeyVal returns the key and value type produced by a range clause
|
||||
// over an expression of type typ.
|
||||
// over an expression of type orig.
|
||||
// If allowVersion != nil, it is used to check the required language version.
|
||||
// If the range clause is not permitted, rangeKeyVal returns ok = false.
|
||||
// When ok = false, rangeKeyVal may also return a reason in cause.
|
||||
func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
|
||||
// The check parameter is only used in case of an error; it may be nil.
|
||||
func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
|
||||
bad := func(cause string) (Type, Type, string, bool) {
|
||||
return Typ[Invalid], Typ[Invalid], cause, false
|
||||
}
|
||||
|
||||
orig := typ
|
||||
switch typ := arrayPtrDeref(coreType(typ)).(type) {
|
||||
case nil:
|
||||
return bad("no core type")
|
||||
var cause1 string
|
||||
rtyp := sharedUnderOrChan(check, orig, &cause1)
|
||||
if rtyp == nil {
|
||||
return bad(cause1)
|
||||
}
|
||||
|
||||
switch typ := arrayPtrDeref(rtyp).(type) {
|
||||
case *Basic:
|
||||
if isString(typ) {
|
||||
return Typ[Int], universeRune, "", true // use 'rune' name
|
||||
|
|
@ -1022,9 +1025,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
|
|||
case *Map:
|
||||
return typ.key, typ.elem, "", true
|
||||
case *Chan:
|
||||
if typ.dir == SendOnly {
|
||||
return bad("receive from send-only channel")
|
||||
}
|
||||
assert(typ.dir != SendOnly)
|
||||
return typ.elem, nil, "", true
|
||||
case *Signature:
|
||||
if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,57 @@ func typeset(t Type, yield func(t, u Type) bool) {
|
|||
yield(t, under(t))
|
||||
}
|
||||
|
||||
// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
|
||||
// if that type is a channel type it must permit receive operations.
|
||||
// If t is a type parameter, sharedUnderOrChan returns the single underlying
|
||||
// type of all types in its type set if it exists, or, if the type set contains
|
||||
// only channel types permitting receive operations and with identical element
|
||||
// types, sharedUnderOrChan returns one of those channel types.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil cause
|
||||
// is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func sharedUnderOrChan(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
var sc *Chan
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
c, _ := u.(*Chan)
|
||||
if c != nil && c.dir == SendOnly {
|
||||
return bad(check.sprintf("receive from send-only channel %s", t))
|
||||
}
|
||||
if su == nil {
|
||||
s, su = t, u
|
||||
sc = c // possibly nil
|
||||
return true
|
||||
}
|
||||
// su != nil
|
||||
if sc != nil && c != nil {
|
||||
if !Identical(sc.elem, c.elem) {
|
||||
return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
|
||||
}
|
||||
return true
|
||||
}
|
||||
// sc == nil
|
||||
if !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, coreType returns the underlying type.
|
||||
// If t is a type parameter, coreType returns the single underlying
|
||||
// type of all types in its type set if it exists, or nil otherwise. If the
|
||||
|
|
|
|||
|
|
@ -877,8 +877,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|||
// determine key/value types
|
||||
var key, val Type
|
||||
if x.mode != invalid {
|
||||
// Ranging over a type parameter is permitted if it has a core type.
|
||||
k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
|
||||
k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
|
||||
return check.allowVersion(v)
|
||||
})
|
||||
switch {
|
||||
|
|
@ -1010,19 +1009,23 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|||
}
|
||||
|
||||
// rangeKeyVal returns the key and value type produced by a range clause
|
||||
// over an expression of type typ.
|
||||
// over an expression of type orig.
|
||||
// If allowVersion != nil, it is used to check the required language version.
|
||||
// If the range clause is not permitted, rangeKeyVal returns ok = false.
|
||||
// When ok = false, rangeKeyVal may also return a reason in cause.
|
||||
func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
|
||||
// The check parameter is only used in case of an error; it may be nil.
|
||||
func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
|
||||
bad := func(cause string) (Type, Type, string, bool) {
|
||||
return Typ[Invalid], Typ[Invalid], cause, false
|
||||
}
|
||||
|
||||
orig := typ
|
||||
switch typ := arrayPtrDeref(coreType(typ)).(type) {
|
||||
case nil:
|
||||
return bad("no core type")
|
||||
var cause1 string
|
||||
rtyp := sharedUnderOrChan(check, orig, &cause1)
|
||||
if rtyp == nil {
|
||||
return bad(cause1)
|
||||
}
|
||||
|
||||
switch typ := arrayPtrDeref(rtyp).(type) {
|
||||
case *Basic:
|
||||
if isString(typ) {
|
||||
return Typ[Int], universeRune, "", true // use 'rune' name
|
||||
|
|
@ -1040,9 +1043,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
|
|||
case *Map:
|
||||
return typ.key, typ.elem, "", true
|
||||
case *Chan:
|
||||
if typ.dir == SendOnly {
|
||||
return bad("receive from send-only channel")
|
||||
}
|
||||
assert(typ.dir != SendOnly)
|
||||
return typ.elem, nil, "", true
|
||||
case *Signature:
|
||||
if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,57 @@ func typeset(t Type, yield func(t, u Type) bool) {
|
|||
yield(t, under(t))
|
||||
}
|
||||
|
||||
// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
|
||||
// if that type is a channel type it must permit receive operations.
|
||||
// If t is a type parameter, sharedUnderOrChan returns the single underlying
|
||||
// type of all types in its type set if it exists, or, if the type set contains
|
||||
// only channel types permitting receive operations and with identical element
|
||||
// types, sharedUnderOrChan returns one of those channel types.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil cause
|
||||
// is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func sharedUnderOrChan(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
var sc *Chan
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
c, _ := u.(*Chan)
|
||||
if c != nil && c.dir == SendOnly {
|
||||
return bad(check.sprintf("receive from send-only channel %s", t))
|
||||
}
|
||||
if su == nil {
|
||||
s, su = t, u
|
||||
sc = c // possibly nil
|
||||
return true
|
||||
}
|
||||
// su != nil
|
||||
if sc != nil && c != nil {
|
||||
if !Identical(sc.elem, c.elem) {
|
||||
return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
|
||||
}
|
||||
return true
|
||||
}
|
||||
// sc == nil
|
||||
if !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, coreType returns the underlying type.
|
||||
// If t is a type parameter, coreType returns the single underlying
|
||||
// type of all types in its type set if it exists, or nil otherwise. If the
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ func _[
|
|||
for _, _ = range s1 {}
|
||||
|
||||
var s2 S2
|
||||
for range s2 /* ERRORx `cannot range over s2.*no core type` */ {}
|
||||
for range s2 /* ERRORx `cannot range over s2.*\[\]int and \[10\]int have different underlying types` */ {}
|
||||
|
||||
var a0 []int
|
||||
for range a0 {}
|
||||
|
|
@ -243,7 +243,7 @@ func _[
|
|||
for _, _ = range a1 {}
|
||||
|
||||
var a2 A2
|
||||
for range a2 /* ERRORx `cannot range over a2.*no core type` */ {}
|
||||
for range a2 /* ERRORx `cannot range over a2.*\[10\]int and \[\]int have different underlying types` */ {}
|
||||
|
||||
var p0 *[10]int
|
||||
for range p0 {}
|
||||
|
|
@ -256,7 +256,7 @@ func _[
|
|||
for _, _ = range p1 {}
|
||||
|
||||
var p2 P2
|
||||
for range p2 /* ERRORx `cannot range over p2.*no core type` */ {}
|
||||
for range p2 /* ERRORx `cannot range over p2.*\*\[10\]int and \*\[\]int have different underlying types` */ {}
|
||||
|
||||
var m0 map[string]int
|
||||
for range m0 {}
|
||||
|
|
@ -269,7 +269,7 @@ func _[
|
|||
for _, _ = range m1 {}
|
||||
|
||||
var m2 M2
|
||||
for range m2 /* ERRORx `cannot range over m2.*no core type` */ {}
|
||||
for range m2 /* ERRORx `cannot range over m2.*map\[string\]int and map\[string\]string` */ {}
|
||||
}
|
||||
|
||||
// type inference checks
|
||||
|
|
|
|||
|
|
@ -129,13 +129,23 @@ func test() {
|
|||
}
|
||||
}
|
||||
|
||||
func _[T any](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by any): no specific type" */ {
|
||||
}
|
||||
}
|
||||
|
||||
func _[T interface{int; string}](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by interface{int; string} with empty type set): no specific type" */ {
|
||||
}
|
||||
}
|
||||
|
||||
func _[T int | string](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): no core type" */ {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): int and string have different underlying types" */ {
|
||||
}
|
||||
}
|
||||
|
||||
func _[T int | int64](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): no core type" */ {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): int and int64 have different underlying types" */ {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,12 +56,12 @@ func _() {
|
|||
}
|
||||
|
||||
func _[T int | string](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): no core type" */ {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): int and string have different underlying types" */ {
|
||||
}
|
||||
}
|
||||
|
||||
func _[T int | int64](x T) {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): no core type" */ {
|
||||
for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): int and int64 have different underlying types" */ {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue