[dev.typeparams] cmd/compile/internal/types2: replace optype() with under() in various cases (cleanup)

This makes the behavior for type parameter operands explicit
in those cases.

Change-Id: I38438af67de4432f1a691dc4947e4576445f031b
Reviewed-on: https://go-review.googlesource.com/c/go/+/332555
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-07-02 16:54:14 -07:00
parent 03ec8de24b
commit d2bf94fb86
6 changed files with 21 additions and 19 deletions

View file

@ -332,13 +332,15 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
return return
} }
var src Type var src Type
switch t := optype(y.typ).(type) { switch t := under(y.typ).(type) {
case *Basic: case *Basic:
if isString(y.typ) { if isString(y.typ) {
src = universeByte src = universeByte
} }
case *Slice: case *Slice:
src = t.elem src = t.elem
case *TypeParam:
check.error(x, "copy on generic operands not yet implemented")
} }
if dst == nil || src == nil { if dst == nil || src == nil {
@ -455,12 +457,12 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
var valid func(t Type) bool var valid func(t Type) bool
valid = func(t Type) bool { valid = func(t Type) bool {
var m int var m int
switch t := optype(t).(type) { switch t := under(t).(type) {
case *Slice: case *Slice:
m = 2 m = 2
case *Map, *Chan: case *Map, *Chan:
m = 1 m = 1
case *Union: case *TypeParam:
return t.underIs(valid) return t.underIs(valid)
default: default:
return false return false

View file

@ -691,7 +691,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion return nil, nil, _InvalidUntypedConversion
} }
switch t := optype(target).(type) { switch t := under(target).(type) {
case *Basic: case *Basic:
if x.mode == constant_ { if x.mode == constant_ {
v, code := check.representation(x, t) v, code := check.representation(x, t)
@ -723,7 +723,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
default: default:
return nil, nil, _InvalidUntypedConversion return nil, nil, _InvalidUntypedConversion
} }
case *Union: case *TypeParam:
ok := t.underIs(func(t Type) bool { ok := t.underIs(func(t Type) bool {
target, _, _ := check.implicitTypeAndValue(x, t) target, _, _ := check.implicitTypeAndValue(x, t)
return target != nil return target != nil
@ -1197,7 +1197,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
goto Error goto Error
} }
switch utyp := optype(base).(type) { switch utyp := under(base).(type) {
case *Struct: case *Struct:
if len(e.ElemList) == 0 { if len(e.ElemList) == 0 {
break break

View file

@ -199,7 +199,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
valid := false valid := false
length := int64(-1) // valid if >= 0 length := int64(-1) // valid if >= 0
switch typ := optype(x.typ).(type) { switch typ := under(x.typ).(type) {
case *Basic: case *Basic:
if isString(typ) { if isString(typ) {
if e.Full { if e.Full {
@ -239,7 +239,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
valid = true valid = true
// x.typ doesn't change // x.typ doesn't change
case *Union, *TypeParam: case *TypeParam:
check.error(x, "generic slice expressions not yet implemented") check.error(x, "generic slice expressions not yet implemented")
x.mode = invalid x.mode = invalid
return return

View file

@ -25,10 +25,10 @@ func isGeneric(typ Type) bool {
} }
func is(typ Type, what BasicInfo) bool { func is(typ Type, what BasicInfo) bool {
switch t := optype(typ).(type) { switch t := under(typ).(type) {
case *Basic: case *Basic:
return t.info&what != 0 return t.info&what != 0
case *Union: case *TypeParam:
return t.underIs(func(t Type) bool { return is(t, what) }) return t.underIs(func(t Type) bool { return is(t, what) })
} }
return false return false
@ -56,7 +56,7 @@ func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }
// are not fully set up. // are not fully set up.
func isTyped(typ Type) bool { func isTyped(typ Type) bool {
// isTyped is called with types that are not fully // isTyped is called with types that are not fully
// set up. Must not call Basic()! // set up. Must not call asBasic()!
// A *Named or *instance type is always typed, so // A *Named or *instance type is always typed, so
// we only need to check if we have a true *Basic // we only need to check if we have a true *Basic
// type. // type.
@ -97,18 +97,19 @@ func comparable(T Type, seen map[Type]bool) bool {
seen[T] = true seen[T] = true
// If T is a type parameter not constrained by any type // If T is a type parameter not constrained by any type
// list (i.e., it's operational type is the top type), // (i.e., it's operational type is the top type),
// T is comparable if it has the == method. Otherwise, // T is comparable if it has the == method. Otherwise,
// the operational type "wins". For instance // the operational type "wins". For instance
// //
// interface{ comparable; type []byte } // interface{ comparable; type []byte }
// //
// is not comparable because []byte is not comparable. // is not comparable because []byte is not comparable.
// TODO(gri) this code is not 100% correct (see comment for TypeSet.IsComparable)
if t := asTypeParam(T); t != nil && optype(t) == theTop { if t := asTypeParam(T); t != nil && optype(t) == theTop {
return t.Bound().IsComparable() return t.Bound().IsComparable()
} }
switch t := optype(T).(type) { switch t := under(T).(type) {
case *Basic: case *Basic:
// assume invalid types to be comparable // assume invalid types to be comparable
// to avoid follow-up errors // to avoid follow-up errors
@ -124,24 +125,22 @@ func comparable(T Type, seen map[Type]bool) bool {
return true return true
case *Array: case *Array:
return comparable(t.elem, seen) return comparable(t.elem, seen)
case *Union: case *TypeParam:
return t.underIs(func(t Type) bool { return t.underIs(func(t Type) bool {
return comparable(t, seen) return comparable(t, seen)
}) })
case *TypeParam:
return t.Bound().IsComparable()
} }
return false return false
} }
// hasNil reports whether a type includes the nil value. // hasNil reports whether a type includes the nil value.
func hasNil(typ Type) bool { func hasNil(typ Type) bool {
switch t := optype(typ).(type) { switch t := under(typ).(type) {
case *Basic: case *Basic:
return t.kind == UnsafePointer return t.kind == UnsafePointer
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true return true
case *Union: case *TypeParam:
return t.underIs(hasNil) return t.underIs(hasNil)
} }
return false return false

View file

@ -119,7 +119,7 @@ func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[
// slicing // slicing
// TODO(gri) implement this // TODO(gri) implement this
func _[T interface{ ~string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] } func _[T interface{ ~string }] (x T, i, j, k int) { _ = x /* ERROR generic slice expressions not yet implemented */ [i:j:k] }
// len/cap built-ins // len/cap built-ins

View file

@ -28,6 +28,7 @@ func (s *TypeSet) IsTop() bool { return len(s.methods) == 0 && s.types == nil }
func (s *TypeSet) IsMethodSet() bool { return s.types == nil && !s.IsComparable() } func (s *TypeSet) IsMethodSet() bool { return s.types == nil && !s.IsComparable() }
// IsComparable reports whether each type in the set is comparable. // IsComparable reports whether each type in the set is comparable.
// TODO(gri) this is not correct - there may be s.types values containing non-comparable types
func (s *TypeSet) IsComparable() bool { func (s *TypeSet) IsComparable() bool {
_, m := s.LookupMethod(nil, "==") _, m := s.LookupMethod(nil, "==")
return m != nil return m != nil