Added/clarified some comments.
Change-Id: Ib08d3343ff08c23cc8880a27a0148d1ff077a80f
Reviewed-on: https://go-review.googlesource.com/c/go/+/363654
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
}
case *Interface:
- if tparamIsIface && isTypeParam(x.typ) {
- if t.typeSet().underIs(func(t Type) bool {
- switch t := arrayPtrDeref(t).(type) {
- case *Basic:
- if isString(t) && id == _Len {
- return true
- }
- case *Array, *Slice, *Chan:
- return true
- case *Map:
- if id == _Len {
- return true
- }
- }
- return false
- }) {
- mode = value
- }
+ if !isTypeParam(x.typ) {
+ break
}
- case *TypeParam:
- assert(!tparamIsIface)
- if t.underIs(func(t Type) bool {
+ if t.typeSet().underIs(func(t Type) bool {
switch t := arrayPtrDeref(t).(type) {
case *Basic:
if isString(t) && id == _Len {
}
case *Interface:
return isTypeParam(t)
- case *TypeParam:
- assert(!tparamIsIface)
- return true
case *Named, *Union:
unreachable()
}
check.later(func() {
for i, bound := range bounds {
if isTypeParam(bound) {
+ // We may be able to allow this since it is now well-defined what
+ // the underlying type and thus type set of a type parameter is.
+ // But we may need some additional form of cycle detection within
+ // type parameter lists.
check.error(posers[i], "cannot use a type parameter as constraint")
}
}
newType, val, code := check.implicitTypeAndValue(x, target)
if code != 0 {
t := target
- if !tparamIsIface || !isTypeParam(target) {
+ if !isTypeParam(target) {
t = safeUnderlying(target)
}
check.invalidConversion(code, x, t)
default:
return nil, nil, _InvalidUntypedConversion
}
- case *TypeParam:
- assert(!tparamIsIface)
- if !u.underIs(func(u Type) bool {
- if u == nil {
- return false
- }
- t, _, _ := check.implicitTypeAndValue(x, u)
- return t != nil
- }) {
- return nil, nil, _InvalidUntypedConversion
- }
case *Interface:
- if tparamIsIface && isTypeParam(target) {
+ if isTypeParam(target) {
if !u.typeSet().underIs(func(u Type) bool {
if u == nil {
return false
return false
case *Interface:
- // Note: The body of this 'if' statement is the same as the body
- // of the case for type parameters below. If we keep both
- // these branches we should factor out the code.
- if tparamIsIface && isTypeParam(x.typ) {
- // TODO(gri) report detailed failure cause for better error messages
- var key, elem Type // key != nil: we must have all maps
- mode := variable // non-maps result mode
- // TODO(gri) factor out closure and use it for non-typeparam cases as well
- if typ.typeSet().underIs(func(u Type) bool {
- l := int64(-1) // valid if >= 0
- var k, e Type // k is only set for maps
- switch t := u.(type) {
- case *Basic:
- if isString(t) {
- e = universeByte
- mode = value
- }
- case *Array:
- l = t.len
- e = t.elem
- if x.mode != variable {
- mode = value
- }
- case *Pointer:
- if t, _ := under(t.base).(*Array); t != nil {
- l = t.len
- e = t.elem
- }
- case *Slice:
- e = t.elem
- case *Map:
- k = t.key
- e = t.elem
- }
- if e == nil {
- return false
- }
- if elem == nil {
- // first type
- length = l
- key, elem = k, e
- return true
- }
- // all map keys must be identical (incl. all nil)
- // (that is, we cannot mix maps with other types)
- if !Identical(key, k) {
- return false
- }
- // all element types must be identical
- if !Identical(elem, e) {
- return false
- }
- // track the minimal length for arrays, if any
- if l >= 0 && l < length {
- length = l
- }
- return true
- }) {
- // For maps, the index expression must be assignable to the map key type.
- if key != nil {
- index := check.singleIndex(e)
- if index == nil {
- x.mode = invalid
- return false
- }
- var k operand
- check.expr(&k, index)
- check.assignment(&k, key, "map index")
- // ok to continue even if indexing failed - map element type is known
- x.mode = mapindex
- x.typ = elem
- x.expr = e
- return false
- }
-
- // no maps
- valid = true
- x.mode = mode
- x.typ = elem
- }
+ if !isTypeParam(x.typ) {
+ break
}
- case *TypeParam:
- // Note: The body of this case is the same as the body of the 'if'
- // statement in the interface case above. If we keep both
- // these branches we should factor out the code.
// TODO(gri) report detailed failure cause for better error messages
- assert(!tparamIsIface)
var key, elem Type // key != nil: we must have all maps
mode := variable // non-maps result mode
// TODO(gri) factor out closure and use it for non-typeparam cases as well
- if typ.underIs(func(u Type) bool {
+ if typ.typeSet().underIs(func(u Type) bool {
l := int64(-1) // valid if >= 0
var k, e Type // k is only set for maps
switch t := u.(type) {
case *Array:
return comparable(t.elem, seen)
case *Interface:
- if tparamIsIface && isTypeParam(T) {
- return t.IsComparable()
- }
- return true
- case *TypeParam:
- assert(!tparamIsIface)
- return t.iface().IsComparable()
+ return !isTypeParam(T) || t.IsComparable()
}
return false
}
case *Slice, *Pointer, *Signature, *Map, *Chan:
return true
case *Interface:
- if tparamIsIface && isTypeParam(t) {
- return u.typeSet().underIs(func(u Type) bool {
- return u != nil && hasNil(u)
- })
- }
- return true
- case *TypeParam:
- assert(!tparamIsIface)
- return u.underIs(func(u Type) bool {
+ return !isTypeParam(t) || u.typeSet().underIs(func(u Type) bool {
return u != nil && hasNil(u)
})
}
case *Slice, *Interface:
// Multiword data structures are effectively structs
// in which each element has size WordSize.
- assert(!tparamIsIface || !isTypeParam(T))
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Alignof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize
case *Basic:
// Strings are like slices and interfaces.
offsets := s.Offsetsof(t.fields)
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
case *Interface:
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Sizeof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize * 2
case *TypeParam, *Union:
unreachable()
}
case *Pointer:
check.error(embeddedPos, "embedded field type cannot be a pointer")
- case *TypeParam:
- assert(!tparamIsIface)
- check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter")
case *Interface:
- if tparamIsIface && isTypeParam(t) {
+ if isTypeParam(t) {
check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter")
break
}
case *Named:
return t.under()
case *TypeParam:
- if tparamIsIface {
- return t.iface()
- }
+ return t.iface()
}
return t
}
import "sync/atomic"
-// If set, the underlying type of a type parameter is
-// is the underlying type of its type constraint, i.e.,
-// an interface. With that, a type parameter satisfies
-// isInterface.
-const tparamIsIface = true
-
// Note: This is a uint32 rather than a uint64 because the
// respective 64 bit atomic instructions are not available
// on all platforms.
}
func (t *TypeParam) Underlying() Type {
- if tparamIsIface {
- return t.iface()
- }
- return t
+ return t.iface()
}
func (t *TypeParam) String() string { return TypeString(t, nil) }
return &emptyInterface
}
case *Interface:
- if tparamIsIface && isTypeParam(bound) {
+ if isTypeParam(bound) {
// error is reported in Checker.collectTypeParams
return &emptyInterface
}
ityp = u
- case *TypeParam:
- assert(!tparamIsIface)
- // error is reported in Checker.collectTypeParams
- return &emptyInterface
}
// If we don't have an interface, wrap constraint into an implicit interface.