mirror of
https://github.com/golang/go
synced 2024-11-24 11:30:13 -07:00
go/types: implement singleType and structure (type)
This is a clean port of CL 359015 to go/types. Change-Id: Iea4e3bfe0a4ae0e5a9052cb6e66c01405bd57c3d Reviewed-on: https://go-review.googlesource.com/c/go/+/360756 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
60fd3ed2b1
commit
80065cf2f6
@ -83,7 +83,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
// of S and the respective parameter passing rules apply."
|
// of S and the respective parameter passing rules apply."
|
||||||
S := x.typ
|
S := x.typ
|
||||||
var T Type
|
var T Type
|
||||||
if s, _ := singleUnder(S).(*Slice); s != nil {
|
if s, _ := structure(S).(*Slice); s != nil {
|
||||||
T = s.elem
|
T = s.elem
|
||||||
} else {
|
} else {
|
||||||
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
|
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
|
||||||
@ -332,14 +332,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
|
|
||||||
case _Copy:
|
case _Copy:
|
||||||
// copy(x, y []T) int
|
// copy(x, y []T) int
|
||||||
dst, _ := singleUnder(x.typ).(*Slice)
|
dst, _ := structure(x.typ).(*Slice)
|
||||||
|
|
||||||
var y operand
|
var y operand
|
||||||
arg(&y, 1)
|
arg(&y, 1)
|
||||||
if y.mode == invalid {
|
if y.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
src, _ := singleUnderString(y.typ).(*Slice)
|
src, _ := structureString(y.typ).(*Slice)
|
||||||
|
|
||||||
if dst == nil || src == nil {
|
if dst == nil || src == nil {
|
||||||
check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
|
check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
|
||||||
@ -473,7 +473,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
}
|
}
|
||||||
|
|
||||||
var min int // minimum number of arguments
|
var min int // minimum number of arguments
|
||||||
switch singleUnder(T).(type) {
|
switch structure(T).(type) {
|
||||||
case *Slice:
|
case *Slice:
|
||||||
min = 2
|
min = 2
|
||||||
case *Map, *Chan:
|
case *Map, *Chan:
|
||||||
@ -776,11 +776,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If typ is a type parameter, single under returns the single underlying
|
// If typ is a type parameter, structure returns the single underlying
|
||||||
// type of all types in the corresponding type constraint if it exists, or
|
// type of all types in the corresponding type constraint if it exists,
|
||||||
// nil if it doesn't exist. If typ is not a type parameter, singleUnder
|
// or nil otherwise. If typ is not a type parameter, structure returns
|
||||||
// just returns the underlying type.
|
// the underlying type.
|
||||||
func singleUnder(typ Type) Type {
|
func structure(typ Type) Type {
|
||||||
var su Type
|
var su Type
|
||||||
if underIs(typ, func(u Type) bool {
|
if underIs(typ, func(u Type) bool {
|
||||||
if su != nil && !Identical(su, u) {
|
if su != nil && !Identical(su, u) {
|
||||||
@ -795,10 +795,10 @@ func singleUnder(typ Type) Type {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// singleUnderString is like singleUnder but also considers []byte and
|
// structureString is like structure but also considers []byte and
|
||||||
// string as "identical". In this case, if successful, the result is always
|
// string as "identical". In this case, if successful, the result
|
||||||
// []byte.
|
// is always []byte.
|
||||||
func singleUnderString(typ Type) Type {
|
func structureString(typ Type) Type {
|
||||||
var su Type
|
var su Type
|
||||||
if underIs(typ, func(u Type) bool {
|
if underIs(typ, func(u Type) bool {
|
||||||
if isString(u) {
|
if isString(u) {
|
||||||
|
@ -175,7 +175,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||||||
cgocall := x.mode == cgofunc
|
cgocall := x.mode == cgofunc
|
||||||
|
|
||||||
// a type parameter may be "called" if all types have the same signature
|
// a type parameter may be "called" if all types have the same signature
|
||||||
sig, _ := singleUnder(x.typ).(*Signature)
|
sig, _ := structure(x.typ).(*Signature)
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
|
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
|
@ -1227,7 +1227,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
switch utyp := singleUnder(base).(type) {
|
switch utyp := structure(base).(type) {
|
||||||
case *Struct:
|
case *Struct:
|
||||||
if len(e.Elts) == 0 {
|
if len(e.Elts) == 0 {
|
||||||
break
|
break
|
||||||
|
@ -207,7 +207,7 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
|
|||||||
|
|
||||||
valid := false
|
valid := false
|
||||||
length := int64(-1) // valid if >= 0
|
length := int64(-1) // valid if >= 0
|
||||||
switch u := singleUnder(x.typ).(type) {
|
switch u := structure(x.typ).(type) {
|
||||||
case nil:
|
case nil:
|
||||||
check.errorf(x, _NonSliceableOperand, "cannot slice %s: type set has no single underlying type", x)
|
check.errorf(x, _NonSliceableOperand, "cannot slice %s: type set has no single underlying type", x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
|
@ -358,7 +358,7 @@ func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
|
|||||||
func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type, index int) {
|
func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type, index int) {
|
||||||
assert(len(tparams) >= len(targs) && len(targs) > 0)
|
assert(len(tparams) >= len(targs) && len(targs) > 0)
|
||||||
|
|
||||||
// Setup bidirectional unification between those structural bounds
|
// Setup bidirectional unification between constraints
|
||||||
// and the corresponding type arguments (which may be nil!).
|
// and the corresponding type arguments (which may be nil!).
|
||||||
u := newUnifier(false)
|
u := newUnifier(false)
|
||||||
u.x.init(tparams)
|
u.x.init(tparams)
|
||||||
@ -371,11 +371,16 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unify type parameters with their structural constraints, if any.
|
// If a constraint has a structural type, unify the corresponding type parameter with it.
|
||||||
for _, tpar := range tparams {
|
for _, tpar := range tparams {
|
||||||
typ := tpar
|
typ := tpar
|
||||||
sbound := typ.structuralType()
|
sbound := structure(tpar)
|
||||||
if sbound != nil {
|
if sbound != nil {
|
||||||
|
// If the structural type is the underlying type of a single
|
||||||
|
// defined type in the constraint, use that defined type instead.
|
||||||
|
if named, _ := tpar.singleType().(*Named); named != nil {
|
||||||
|
sbound = named
|
||||||
|
}
|
||||||
if !u.unify(typ, sbound) {
|
if !u.unify(typ, sbound) {
|
||||||
check.errorf(tpar.obj, _Todo, "%s does not match %s", tpar.obj, sbound)
|
check.errorf(tpar.obj, _Todo, "%s does not match %s", tpar.obj, sbound)
|
||||||
return nil, 0
|
return nil, 0
|
||||||
@ -384,7 +389,7 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// u.x.types() now contains the incoming type arguments plus any additional type
|
// u.x.types() now contains the incoming type arguments plus any additional type
|
||||||
// arguments for which there were structural constraints. The newly inferred non-
|
// arguments which were inferred from structural types. The newly inferred non-
|
||||||
// nil entries may still contain references to other type parameters.
|
// nil entries may still contain references to other type parameters.
|
||||||
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
|
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
|
||||||
// was given, unification produced the type list [int, []C, *A]. We eliminate the
|
// was given, unification produced the type list [int, []C, *A]. We eliminate the
|
||||||
|
@ -834,7 +834,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
// Ranging over a type parameter is permitted if it has a single underlying type.
|
// Ranging over a type parameter is permitted if it has a single underlying type.
|
||||||
var cause string
|
var cause string
|
||||||
u := singleUnder(x.typ)
|
u := structure(x.typ)
|
||||||
switch t := u.(type) {
|
switch t := u.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
cause = "type set has no single underlying type"
|
cause = "type set has no single underlying type"
|
||||||
|
@ -93,8 +93,8 @@ func (xl termlist) norm() termlist {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the type set represented by xl is specified by a single (non-𝓤) term,
|
// If the type set represented by xl is specified by a single (non-𝓤) term,
|
||||||
// structuralType returns that type. Otherwise it returns nil.
|
// singleType returns that type. Otherwise it returns nil.
|
||||||
func (xl termlist) structuralType() Type {
|
func (xl termlist) singleType() Type {
|
||||||
if nl := xl.norm(); len(nl) == 1 {
|
if nl := xl.norm(); len(nl) == 1 {
|
||||||
return nl[0].typ // if nl.isAll() then typ is nil, which is ok
|
return nl[0].typ // if nl.isAll() then typ is nil, which is ok
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ func TestTermlistNorm(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTermlistStructuralType(t *testing.T) {
|
func TestTermlistSingleType(t *testing.T) {
|
||||||
// helper to deal with nil types
|
// helper to deal with nil types
|
||||||
tstring := func(typ Type) string {
|
tstring := func(typ Type) string {
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
@ -128,9 +128,9 @@ func TestTermlistStructuralType(t *testing.T) {
|
|||||||
"∅ ∪ ~int ∪ string": "nil",
|
"∅ ∪ ~int ∪ string": "nil",
|
||||||
} {
|
} {
|
||||||
xl := maketl(test)
|
xl := maketl(test)
|
||||||
got := tstring(xl.structuralType())
|
got := tstring(xl.singleType())
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("(%v).structuralType() == %v; want %v", test, got, want)
|
t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/go/types/testdata/examples/inference.go2
vendored
23
src/go/types/testdata/examples/inference.go2
vendored
@ -99,3 +99,26 @@ func _() {
|
|||||||
related2(1.0, []int{})
|
related2(1.0, []int{})
|
||||||
related2 /* ERROR does not satisfy */ (float64(1.0), []int{})
|
related2 /* ERROR does not satisfy */ (float64(1.0), []int{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type List[P any] []P
|
||||||
|
|
||||||
|
func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil }
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// related3 can be instantiated explicitly
|
||||||
|
related3[int, []int]()
|
||||||
|
related3[byte, List[byte]]()
|
||||||
|
|
||||||
|
// Alternatively, the 2nd type argument can be inferred
|
||||||
|
// from the first one through constraint type inference.
|
||||||
|
related3[int]()
|
||||||
|
|
||||||
|
// The inferred type is the structural type of the Slice
|
||||||
|
// type parameter.
|
||||||
|
var _ []int = related3[int]()
|
||||||
|
|
||||||
|
// It is not the defined parameterized type List.
|
||||||
|
type anotherList []float32
|
||||||
|
var _ anotherList = related3[float32]() // valid
|
||||||
|
var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
|
||||||
|
}
|
||||||
|
@ -118,9 +118,9 @@ func (t *TypeParam) iface() *Interface {
|
|||||||
return ityp
|
return ityp
|
||||||
}
|
}
|
||||||
|
|
||||||
// structuralType returns the structural type of the type parameter's constraint; or nil.
|
// singleType returns the single type of the type parameter constraint; or nil.
|
||||||
func (t *TypeParam) structuralType() Type {
|
func (t *TypeParam) singleType() Type {
|
||||||
return t.iface().typeSet().structuralType()
|
return t.iface().typeSet().singleType()
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasTerms reports whether the type parameter constraint has specific type terms.
|
// hasTerms reports whether the type parameter constraint has specific type terms.
|
||||||
|
@ -102,8 +102,8 @@ func (s *_TypeSet) String() string {
|
|||||||
// hasTerms reports whether the type set has specific type terms.
|
// hasTerms reports whether the type set has specific type terms.
|
||||||
func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() }
|
func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() }
|
||||||
|
|
||||||
// structuralType returns the single type in s if there is exactly one; otherwise the result is nil.
|
// singleType returns the single type in s if there is exactly one; otherwise the result is nil.
|
||||||
func (s *_TypeSet) structuralType() Type { return s.terms.structuralType() }
|
func (s *_TypeSet) singleType() Type { return s.terms.singleType() }
|
||||||
|
|
||||||
// includes reports whether t ∈ s.
|
// includes reports whether t ∈ s.
|
||||||
func (s *_TypeSet) includes(t Type) bool { return s.terms.includes(t) }
|
func (s *_TypeSet) includes(t Type) bool { return s.terms.includes(t) }
|
||||||
|
Loading…
Reference in New Issue
Block a user