mirror of https://github.com/golang/go.git
go/types: make type bound instantiation optional if bound has one type parameter
If a type bound expects one type parameter, permit omitting explicit
instantiation of the type bound in a type parameter list. In that case,
the type bound is instantiated with each respective type parameter
and becomes that type bound for that type parameter.
For example, given:
type Bound(type T) interface { ... }
the type parameter list:
(type P, Q Bound)
is syntactic sugar for:
(type P Bound(P), Q Bound(Q))
This is a generalization of the former change:
https://team-review.git.corp.google.com/c/golang/go2-dev/+/728411
Passes all.bash.
Change-Id: I0191dcf3fbccf6e777f01dfa8ccef5aba0310213
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/751267
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
c64ef0ed90
commit
8df9c7a8fa
|
|
@ -680,42 +680,50 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
|
|||
// TODO(gri) We should try to delay the IsInterface check
|
||||
// as it may expand a possibly incomplete type.
|
||||
if bound := check.anyType(f.Type); IsInterface(bound) {
|
||||
// If we have exactly one type parameter and the type bound expects exactly
|
||||
// one type argument, permit leaving away the type argument for the type
|
||||
// bound. This allows us to write (type T B(T)) as (type T B) instead.
|
||||
// If the type bound expects exactly one type argument, permit leaving
|
||||
// it away and use the corresponding type parameter as implicit argument.
|
||||
// This allows us to write (type p b(p), q b(q), r b(r)) as (type p, q, r b).
|
||||
if isGeneric(bound) {
|
||||
base := bound.(*Named) // only a *Named type can be generic
|
||||
if len(f.Names) != 1 || len(base.tparams) != 1 {
|
||||
// TODO(gri) make this error message better
|
||||
if len(base.tparams) != 1 {
|
||||
check.errorf(f.Type.Pos(), "cannot use generic type %s without instantiation (more than one type parameter)", bound)
|
||||
bound = Typ[Invalid]
|
||||
goto next
|
||||
}
|
||||
// We have and expect exactly one type parameter.
|
||||
// "Manually" instantiate the bound with the parameter.
|
||||
// We have exactly one type parameter.
|
||||
// "Manually" instantiate the bound with each type
|
||||
// parameter the bound applies to.
|
||||
// TODO(gri) this code (in more general form) is also in
|
||||
// checker.typInternal for the *ast.CallExpr case. Factor?
|
||||
typ := new(instance)
|
||||
typ.check = check
|
||||
typ.pos = f.Type.Pos()
|
||||
typ.base = base
|
||||
typ.targs = []Type{tparams[index].typ}
|
||||
typ.poslist = []token.Pos{f.Names[0].Pos()}
|
||||
// make sure we check instantiation works at least once
|
||||
// and that the resulting type is valid
|
||||
check.atEnd(func() {
|
||||
t := typ.expand()
|
||||
check.validType(t, nil)
|
||||
})
|
||||
// update bound and recorded type
|
||||
bound = typ
|
||||
check.recordTypeAndValue(f.Type, typexpr, typ, nil)
|
||||
for i, name := range f.Names {
|
||||
typ := new(instance)
|
||||
typ.check = check
|
||||
typ.pos = f.Type.Pos()
|
||||
typ.base = base
|
||||
typ.targs = []Type{tparams[index+i].typ}
|
||||
typ.poslist = []token.Pos{name.Pos()}
|
||||
// Make sure we check instantiation works at least once
|
||||
// and that the resulting type is valid.
|
||||
check.atEnd(func() {
|
||||
check.validType(typ.expand(), nil)
|
||||
})
|
||||
// Set bound for each type parameter and record type.
|
||||
setBoundAt(index+i, typ)
|
||||
// We don't have a mechanism to record multiple different types
|
||||
// for a single type bound expression. Just do it for the first
|
||||
// type parameter for now.
|
||||
// TODO(gri) investigate what we need to do here
|
||||
if i == 0 {
|
||||
check.recordTypeAndValue(f.Type, typexpr, typ, nil)
|
||||
}
|
||||
}
|
||||
goto next
|
||||
}
|
||||
// Otherwise, set the bound for each type parameter.
|
||||
for i, _ := range f.Names {
|
||||
setBoundAt(index+i, bound)
|
||||
}
|
||||
} else if bound != Typ[Invalid] {
|
||||
check.errorf(f.Type.Pos(), "%s is not an interface or contract", bound)
|
||||
check.errorf(f.Type.Pos(), "%s is not an interface", bound)
|
||||
}
|
||||
|
||||
next:
|
||||
|
|
@ -843,7 +851,7 @@ func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident)
|
|||
nstr := name.Name
|
||||
if len(nstr) > 0 && nstr[0] == '*' {
|
||||
nstr = nstr[1:]
|
||||
check.errorf(name.Pos(), "warning: *-designation recognized but currently ignored")
|
||||
check.errorf(name.Pos(), `pointer designation not yet supported ("*" is ignored)`)
|
||||
}
|
||||
tpar := NewTypeName(name.Pos(), check.pkg, nstr, nil)
|
||||
check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
|
||||
|
|
|
|||
|
|
@ -202,9 +202,9 @@ func _(type T interface{ m(); type int })() {
|
|||
}
|
||||
|
||||
// As a special case, an explicit type parameter may be omitted
|
||||
// from a type parameter bound if there is exactly one type
|
||||
// parameter to which the type bound applies, and the type bound
|
||||
// expects exactly one type argument.
|
||||
// from a type parameter bound if the type bound expects exactly
|
||||
// one type argument. In that case, the type argument is the
|
||||
// respective type paramater to which the type bound applies.
|
||||
type Adder(type T) interface {
|
||||
Add(T) T
|
||||
}
|
||||
|
|
@ -229,7 +229,19 @@ func _(type T1 B1)()
|
|||
func _(type T1 B2 /* ERROR cannot use generic type .* without instantiation */ )()
|
||||
|
||||
func _(type T1, T2 B0)()
|
||||
func _(type T1, T2 B1 /* ERROR cannot use generic type .* without instantiation */ )()
|
||||
func _(type T1, T2 B1)()
|
||||
func _(type T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ )()
|
||||
|
||||
func _(type T1 B0, T2 B1)() // here B1 applies to T2
|
||||
|
||||
// When the type argument is left away, the type bound is
|
||||
// instantiated for each type parameter with that type
|
||||
// parameter.
|
||||
func _(type A, B Adder, C Adder(A))() {
|
||||
var a A // A's type bound is Adder(A)
|
||||
a = a.Add(a)
|
||||
var b B // B's type bound is Adder(B)
|
||||
b = b.Add(b)
|
||||
var c C // C's type bound is Adder(A)
|
||||
a = c.Add(a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -722,7 +722,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
|||
case "underlying type":
|
||||
types = append(types, f.Type)
|
||||
if firstUnderlying {
|
||||
check.errorf(name.Pos(), `"underlying" designation recognized but currently ignored`)
|
||||
check.errorf(name.Pos(), `"underlying" designation not yet supported ("underlying" is ignored)`)
|
||||
firstUnderlying = false
|
||||
}
|
||||
continue
|
||||
|
|
|
|||
Loading…
Reference in New Issue