Skip to content

Commit 80065cf

Browse files
committed
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 <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 60fd3ed commit 80065cf

11 files changed

Lines changed: 59 additions & 31 deletions

File tree

src/go/types/builtins.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
8383
// of S and the respective parameter passing rules apply."
8484
S := x.typ
8585
var T Type
86-
if s, _ := singleUnder(S).(*Slice); s != nil {
86+
if s, _ := structure(S).(*Slice); s != nil {
8787
T = s.elem
8888
} else {
8989
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
332332

333333
case _Copy:
334334
// copy(x, y []T) int
335-
dst, _ := singleUnder(x.typ).(*Slice)
335+
dst, _ := structure(x.typ).(*Slice)
336336

337337
var y operand
338338
arg(&y, 1)
339339
if y.mode == invalid {
340340
return
341341
}
342-
src, _ := singleUnderString(y.typ).(*Slice)
342+
src, _ := structureString(y.typ).(*Slice)
343343

344344
if dst == nil || src == nil {
345345
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
473473
}
474474

475475
var min int // minimum number of arguments
476-
switch singleUnder(T).(type) {
476+
switch structure(T).(type) {
477477
case *Slice:
478478
min = 2
479479
case *Map, *Chan:
@@ -776,11 +776,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
776776
return true
777777
}
778778

779-
// If typ is a type parameter, single under returns the single underlying
780-
// type of all types in the corresponding type constraint if it exists, or
781-
// nil if it doesn't exist. If typ is not a type parameter, singleUnder
782-
// just returns the underlying type.
783-
func singleUnder(typ Type) Type {
779+
// If typ is a type parameter, structure returns the single underlying
780+
// type of all types in the corresponding type constraint if it exists,
781+
// or nil otherwise. If typ is not a type parameter, structure returns
782+
// the underlying type.
783+
func structure(typ Type) Type {
784784
var su Type
785785
if underIs(typ, func(u Type) bool {
786786
if su != nil && !Identical(su, u) {
@@ -795,10 +795,10 @@ func singleUnder(typ Type) Type {
795795
return nil
796796
}
797797

798-
// singleUnderString is like singleUnder but also considers []byte and
799-
// string as "identical". In this case, if successful, the result is always
800-
// []byte.
801-
func singleUnderString(typ Type) Type {
798+
// structureString is like structure but also considers []byte and
799+
// string as "identical". In this case, if successful, the result
800+
// is always []byte.
801+
func structureString(typ Type) Type {
802802
var su Type
803803
if underIs(typ, func(u Type) bool {
804804
if isString(u) {

src/go/types/call.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
175175
cgocall := x.mode == cgofunc
176176

177177
// a type parameter may be "called" if all types have the same signature
178-
sig, _ := singleUnder(x.typ).(*Signature)
178+
sig, _ := structure(x.typ).(*Signature)
179179
if sig == nil {
180180
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
181181
x.mode = invalid

src/go/types/expr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
12271227
goto Error
12281228
}
12291229

1230-
switch utyp := singleUnder(base).(type) {
1230+
switch utyp := structure(base).(type) {
12311231
case *Struct:
12321232
if len(e.Elts) == 0 {
12331233
break

src/go/types/index.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
207207

208208
valid := false
209209
length := int64(-1) // valid if >= 0
210-
switch u := singleUnder(x.typ).(type) {
210+
switch u := structure(x.typ).(type) {
211211
case nil:
212212
check.errorf(x, _NonSliceableOperand, "cannot slice %s: type set has no single underlying type", x)
213213
x.mode = invalid

src/go/types/infer.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
358358
func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type, index int) {
359359
assert(len(tparams) >= len(targs) && len(targs) > 0)
360360

361-
// Setup bidirectional unification between those structural bounds
361+
// Setup bidirectional unification between constraints
362362
// and the corresponding type arguments (which may be nil!).
363363
u := newUnifier(false)
364364
u.x.init(tparams)
@@ -371,11 +371,16 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
371371
}
372372
}
373373

374-
// Unify type parameters with their structural constraints, if any.
374+
// If a constraint has a structural type, unify the corresponding type parameter with it.
375375
for _, tpar := range tparams {
376376
typ := tpar
377-
sbound := typ.structuralType()
377+
sbound := structure(tpar)
378378
if sbound != nil {
379+
// If the structural type is the underlying type of a single
380+
// defined type in the constraint, use that defined type instead.
381+
if named, _ := tpar.singleType().(*Named); named != nil {
382+
sbound = named
383+
}
379384
if !u.unify(typ, sbound) {
380385
check.errorf(tpar.obj, _Todo, "%s does not match %s", tpar.obj, sbound)
381386
return nil, 0
@@ -384,7 +389,7 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
384389
}
385390

386391
// u.x.types() now contains the incoming type arguments plus any additional type
387-
// arguments for which there were structural constraints. The newly inferred non-
392+
// arguments which were inferred from structural types. The newly inferred non-
388393
// nil entries may still contain references to other type parameters.
389394
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
390395
// was given, unification produced the type list [int, []C, *A]. We eliminate the

src/go/types/stmt.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
834834
if x.mode != invalid {
835835
// Ranging over a type parameter is permitted if it has a single underlying type.
836836
var cause string
837-
u := singleUnder(x.typ)
837+
u := structure(x.typ)
838838
switch t := u.(type) {
839839
case nil:
840840
cause = "type set has no single underlying type"

src/go/types/termlist.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func (xl termlist) norm() termlist {
9393
}
9494

9595
// If the type set represented by xl is specified by a single (non-𝓤) term,
96-
// structuralType returns that type. Otherwise it returns nil.
97-
func (xl termlist) structuralType() Type {
96+
// singleType returns that type. Otherwise it returns nil.
97+
func (xl termlist) singleType() Type {
9898
if nl := xl.norm(); len(nl) == 1 {
9999
return nl[0].typ // if nl.isAll() then typ is nil, which is ok
100100
}

src/go/types/termlist_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func TestTermlistNorm(t *testing.T) {
106106
}
107107
}
108108

109-
func TestTermlistStructuralType(t *testing.T) {
109+
func TestTermlistSingleType(t *testing.T) {
110110
// helper to deal with nil types
111111
tstring := func(typ Type) string {
112112
if typ == nil {
@@ -128,9 +128,9 @@ func TestTermlistStructuralType(t *testing.T) {
128128
"∅ ∪ ~int ∪ string": "nil",
129129
} {
130130
xl := maketl(test)
131-
got := tstring(xl.structuralType())
131+
got := tstring(xl.singleType())
132132
if got != want {
133-
t.Errorf("(%v).structuralType() == %v; want %v", test, got, want)
133+
t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
134134
}
135135
}
136136
}

src/go/types/testdata/examples/inference.go2

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,26 @@ func _() {
9999
related2(1.0, []int{})
100100
related2 /* ERROR does not satisfy */ (float64(1.0), []int{})
101101
}
102+
103+
type List[P any] []P
104+
105+
func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil }
106+
107+
func _() {
108+
// related3 can be instantiated explicitly
109+
related3[int, []int]()
110+
related3[byte, List[byte]]()
111+
112+
// Alternatively, the 2nd type argument can be inferred
113+
// from the first one through constraint type inference.
114+
related3[int]()
115+
116+
// The inferred type is the structural type of the Slice
117+
// type parameter.
118+
var _ []int = related3[int]()
119+
120+
// It is not the defined parameterized type List.
121+
type anotherList []float32
122+
var _ anotherList = related3[float32]() // valid
123+
var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
124+
}

src/go/types/typeparam.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ func (t *TypeParam) iface() *Interface {
118118
return ityp
119119
}
120120

121-
// structuralType returns the structural type of the type parameter's constraint; or nil.
122-
func (t *TypeParam) structuralType() Type {
123-
return t.iface().typeSet().structuralType()
121+
// singleType returns the single type of the type parameter constraint; or nil.
122+
func (t *TypeParam) singleType() Type {
123+
return t.iface().typeSet().singleType()
124124
}
125125

126126
// hasTerms reports whether the type parameter constraint has specific type terms.

0 commit comments

Comments
 (0)