mirror of https://github.com/golang/go.git
go/types, types2: instantiate generic alias types
For #46477. Change-Id: Ifa47d3ff87f67c60fa25654e54194ca8b31ea5a2 Reviewed-on: https://go-review.googlesource.com/c/go/+/567617 Auto-Submit: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
ff2070d939
commit
f294ddeb29
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
package types2
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An Alias represents an alias type.
|
||||
// Whether or not Alias types are created is controlled by the
|
||||
|
|
@ -30,7 +33,10 @@ func NewAlias(obj *TypeName, rhs Type) *Alias {
|
|||
return alias
|
||||
}
|
||||
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
// Obj returns the type name for the declaration defining the alias type a.
|
||||
// For instantiated types, this is same as the type name of the origin type.
|
||||
func (a *Alias) Obj() *TypeName { return a.orig.obj }
|
||||
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Underlying returns the [underlying type] of the alias type a, which is the
|
||||
|
|
@ -125,6 +131,20 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
|||
return a
|
||||
}
|
||||
|
||||
// newAliasInstance creates a new alias instance for the given origin and type
|
||||
// arguments, recording pos as the position of its synthetic object (for error
|
||||
// reporting).
|
||||
func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
||||
assert(len(targs) > 0)
|
||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
||||
res := check.newAlias(obj, rhs)
|
||||
res.orig = orig
|
||||
res.tparams = orig.tparams
|
||||
res.targs = newTypeList(targs)
|
||||
return res
|
||||
}
|
||||
|
||||
func (a *Alias) cleanup() {
|
||||
// Ensure a.actual is set before types are published,
|
||||
// so Unalias is a pure "getter", not a "setter".
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ type genericType interface {
|
|||
}
|
||||
|
||||
// Instantiate instantiates the type orig with the given type arguments targs.
|
||||
// orig must be a *Named or a *Signature type. If there is no error, the
|
||||
// resulting Type is an instantiated type of the same kind (either a *Named or
|
||||
// a *Signature). Methods attached to a *Named type are also instantiated, and
|
||||
// associated with a new *Func that has the same position as the original
|
||||
// method, but nil function scope.
|
||||
// orig must be an *Alias, *Named, or *Signature type. If there is no error,
|
||||
// the resulting Type is an instantiated type of the same kind (*Alias, *Named
|
||||
// or *Signature, respectively).
|
||||
//
|
||||
// Methods attached to a *Named type are also instantiated, and associated with
|
||||
// a new *Func that has the same position as the original method, but nil function
|
||||
// scope.
|
||||
//
|
||||
// If ctxt is non-nil, it may be used to de-duplicate the instance against
|
||||
// previous instances with the same identity. As a special case, generic
|
||||
|
|
@ -35,10 +37,10 @@ type genericType interface {
|
|||
// not guarantee that identical instances are deduplicated in all cases.
|
||||
//
|
||||
// If validate is set, Instantiate verifies that the number of type arguments
|
||||
// and parameters match, and that the type arguments satisfy their
|
||||
// corresponding type constraints. If verification fails, the resulting error
|
||||
// may wrap an *ArgumentError indicating which type argument did not satisfy
|
||||
// its corresponding type parameter constraint, and why.
|
||||
// and parameters match, and that the type arguments satisfy their respective
|
||||
// type constraints. If verification fails, the resulting error may wrap an
|
||||
// *ArgumentError indicating which type argument did not satisfy its type parameter
|
||||
// constraint, and why.
|
||||
//
|
||||
// If validate is not set, Instantiate does not verify the type argument count
|
||||
// or whether the type arguments satisfy their constraints. Instantiate is
|
||||
|
|
@ -101,8 +103,9 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
|||
hashes[i] = ctxt.instanceHash(orig, targs)
|
||||
}
|
||||
|
||||
// If local is non-nil, updateContexts return the type recorded in
|
||||
// local.
|
||||
// Record the result in all contexts.
|
||||
// Prefer to re-use existing types from expanding context, if it exists, to reduce
|
||||
// the memory pinned by the Named type.
|
||||
updateContexts := func(res Type) Type {
|
||||
for i := len(ctxts) - 1; i >= 0; i-- {
|
||||
res = ctxts[i].update(hashes[i], orig, targs, res)
|
||||
|
|
@ -122,6 +125,21 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
|
|||
case *Named:
|
||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||
|
||||
case *Alias:
|
||||
// TODO(gri) is this correct?
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
|
||||
tparams := orig.TypeParams()
|
||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||
if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
|
||||
return Typ[Invalid]
|
||||
}
|
||||
if tparams.Len() == 0 {
|
||||
return orig // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
||||
|
||||
case *Signature:
|
||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@ func hasEmptyTypeset(t Type) bool {
|
|||
// TODO(gri) should we include signatures or assert that they are not present?
|
||||
func isGeneric(t Type) bool {
|
||||
// A parameterized type is only generic if it doesn't have an instantiation already.
|
||||
if alias, _ := t.(*Alias); alias != nil && alias.tparams != nil && alias.targs == nil {
|
||||
return true
|
||||
}
|
||||
named := asNamed(t)
|
||||
return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,17 +96,41 @@ func (subst *subster) typ(typ Type) Type {
|
|||
// nothing to do
|
||||
|
||||
case *Alias:
|
||||
rhs := subst.typ(t.fromRHS)
|
||||
if rhs != t.fromRHS {
|
||||
// This branch cannot be reached because the RHS of an alias
|
||||
// may only contain type parameters of an enclosing function.
|
||||
// Such function bodies are never "instantiated" and thus
|
||||
// substitution is not called on locally declared alias types.
|
||||
// TODO(gri) adjust once parameterized aliases are supported
|
||||
panic("unreachable for unparameterized aliases")
|
||||
// return subst.check.newAlias(t.obj, rhs)
|
||||
// This code follows the code for *Named types closely.
|
||||
// TODO(gri) try to factor better
|
||||
orig := t.Origin()
|
||||
n := orig.TypeParams().Len()
|
||||
if n == 0 {
|
||||
return t // type is not parameterized
|
||||
}
|
||||
|
||||
// TODO(gri) do we need this for Alias types?
|
||||
var newTArgs []Type
|
||||
if t.TypeArgs().Len() != n {
|
||||
return Typ[Invalid] // error reported elsewhere
|
||||
}
|
||||
|
||||
// already instantiated
|
||||
// For each (existing) type argument targ, determine if it needs
|
||||
// to be substituted; i.e., if it is or contains a type parameter
|
||||
// that has a type argument for it.
|
||||
for i, targ := range t.TypeArgs().list() {
|
||||
new_targ := subst.typ(targ)
|
||||
if new_targ != targ {
|
||||
if newTArgs == nil {
|
||||
newTArgs = make([]Type, n)
|
||||
copy(newTArgs, t.TypeArgs().list())
|
||||
}
|
||||
newTArgs[i] = new_targ
|
||||
}
|
||||
}
|
||||
|
||||
if newTArgs == nil {
|
||||
return t // nothing to substitute
|
||||
}
|
||||
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, newTArgs, subst.ctxt)
|
||||
|
||||
case *Array:
|
||||
elem := subst.typOrNil(t.elem)
|
||||
if elem != t.elem {
|
||||
|
|
|
|||
|
|
@ -335,6 +335,10 @@ func (w *typeWriter) typ(typ Type) {
|
|||
|
||||
case *Alias:
|
||||
w.typeName(t.obj)
|
||||
if list := t.targs.list(); len(list) != 0 {
|
||||
// instantiated type
|
||||
w.typeList(list)
|
||||
}
|
||||
if w.ctxt != nil {
|
||||
// TODO(gri) do we need to print the alias type name, too?
|
||||
w.typ(Unalias(t.obj.typ))
|
||||
|
|
|
|||
|
|
@ -453,6 +453,10 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
|||
}()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
setDefType(def, res)
|
||||
}()
|
||||
|
||||
var cause string
|
||||
gtyp := check.genericType(x, &cause)
|
||||
if cause != "" {
|
||||
|
|
@ -462,21 +466,23 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
|||
return gtyp // error already reported
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
targs := check.typeList(xlist)
|
||||
if targs == nil {
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
if orig, _ := gtyp.(*Alias); orig != nil {
|
||||
return check.instance(x.Pos(), orig, targs, nil, check.context())
|
||||
}
|
||||
|
||||
orig := asNamed(gtyp)
|
||||
if orig == nil {
|
||||
panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp))
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
targs := check.typeList(xlist)
|
||||
if targs == nil {
|
||||
setDefType(def, Typ[Invalid]) // avoid errors later due to lazy instantiation
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// create the instance
|
||||
inst := asNamed(check.instance(x.Pos(), orig, targs, nil, check.context()))
|
||||
setDefType(def, inst)
|
||||
|
||||
// orig.tparams may not be set up, so we need to do expansion later.
|
||||
check.later(func() {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func dddErrPos(call *syntax.CallExpr) *syntax.CallExpr {
|
|||
return call
|
||||
}
|
||||
|
||||
// argErrPos returns the node (poser) for reportign an invalid argument count.
|
||||
// argErrPos returns the node (poser) for reporting an invalid argument count.
|
||||
func argErrPos(call *syntax.CallExpr) *syntax.CallExpr { return call }
|
||||
|
||||
// ExprString returns a string representation of x.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
package types
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// An Alias represents an alias type.
|
||||
// Whether or not Alias types are created is controlled by the
|
||||
|
|
@ -33,7 +36,10 @@ func NewAlias(obj *TypeName, rhs Type) *Alias {
|
|||
return alias
|
||||
}
|
||||
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
// Obj returns the type name for the declaration defining the alias type a.
|
||||
// For instantiated types, this is same as the type name of the origin type.
|
||||
func (a *Alias) Obj() *TypeName { return a.orig.obj }
|
||||
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Underlying returns the [underlying type] of the alias type a, which is the
|
||||
|
|
@ -128,6 +134,20 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
|||
return a
|
||||
}
|
||||
|
||||
// newAliasInstance creates a new alias instance for the given origin and type
|
||||
// arguments, recording pos as the position of its synthetic object (for error
|
||||
// reporting).
|
||||
func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, ctxt *Context) *Alias {
|
||||
assert(len(targs) > 0)
|
||||
obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
|
||||
rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), nil, ctxt)
|
||||
res := check.newAlias(obj, rhs)
|
||||
res.orig = orig
|
||||
res.tparams = orig.tparams
|
||||
res.targs = newTypeList(targs)
|
||||
return res
|
||||
}
|
||||
|
||||
func (a *Alias) cleanup() {
|
||||
// Ensure a.actual is set before types are published,
|
||||
// so Unalias is a pure "getter", not a "setter".
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ func generate(t *testing.T, filename string, write bool) {
|
|||
type action func(in *ast.File)
|
||||
|
||||
var filemap = map[string]action{
|
||||
"alias.go": nil,
|
||||
"alias.go": fixTokenPos,
|
||||
"assignments.go": func(f *ast.File) {
|
||||
renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
|
||||
renameSelectorExprs(f, "syntax.Name->ast.Ident", "ident.Value->ident.Name", "ast.Pos->token.Pos") // must happen before renaming identifiers
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ type genericType interface {
|
|||
}
|
||||
|
||||
// Instantiate instantiates the type orig with the given type arguments targs.
|
||||
// orig must be a *Named or a *Signature type. If there is no error, the
|
||||
// resulting Type is an instantiated type of the same kind (either a *Named or
|
||||
// a *Signature). Methods attached to a *Named type are also instantiated, and
|
||||
// associated with a new *Func that has the same position as the original
|
||||
// method, but nil function scope.
|
||||
// orig must be an *Alias, *Named, or *Signature type. If there is no error,
|
||||
// the resulting Type is an instantiated type of the same kind (*Alias, *Named
|
||||
// or *Signature, respectively).
|
||||
//
|
||||
// Methods attached to a *Named type are also instantiated, and associated with
|
||||
// a new *Func that has the same position as the original method, but nil function
|
||||
// scope.
|
||||
//
|
||||
// If ctxt is non-nil, it may be used to de-duplicate the instance against
|
||||
// previous instances with the same identity. As a special case, generic
|
||||
|
|
@ -38,10 +40,10 @@ type genericType interface {
|
|||
// not guarantee that identical instances are deduplicated in all cases.
|
||||
//
|
||||
// If validate is set, Instantiate verifies that the number of type arguments
|
||||
// and parameters match, and that the type arguments satisfy their
|
||||
// corresponding type constraints. If verification fails, the resulting error
|
||||
// may wrap an *ArgumentError indicating which type argument did not satisfy
|
||||
// its corresponding type parameter constraint, and why.
|
||||
// and parameters match, and that the type arguments satisfy their respective
|
||||
// type constraints. If verification fails, the resulting error may wrap an
|
||||
// *ArgumentError indicating which type argument did not satisfy its type parameter
|
||||
// constraint, and why.
|
||||
//
|
||||
// If validate is not set, Instantiate does not verify the type argument count
|
||||
// or whether the type arguments satisfy their constraints. Instantiate is
|
||||
|
|
@ -104,8 +106,9 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
|||
hashes[i] = ctxt.instanceHash(orig, targs)
|
||||
}
|
||||
|
||||
// If local is non-nil, updateContexts return the type recorded in
|
||||
// local.
|
||||
// Record the result in all contexts.
|
||||
// Prefer to re-use existing types from expanding context, if it exists, to reduce
|
||||
// the memory pinned by the Named type.
|
||||
updateContexts := func(res Type) Type {
|
||||
for i := len(ctxts) - 1; i >= 0; i-- {
|
||||
res = ctxts[i].update(hashes[i], orig, targs, res)
|
||||
|
|
@ -125,6 +128,21 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
|
|||
case *Named:
|
||||
res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
|
||||
|
||||
case *Alias:
|
||||
// TODO(gri) is this correct?
|
||||
assert(expanding == nil) // Alias instances cannot be reached from Named types
|
||||
|
||||
tparams := orig.TypeParams()
|
||||
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
|
||||
if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
|
||||
return Typ[Invalid]
|
||||
}
|
||||
if tparams.Len() == 0 {
|
||||
return orig // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
return check.newAliasInstance(pos, orig, targs, ctxt)
|
||||
|
||||
case *Signature:
|
||||
assert(expanding == nil) // function instances cannot be reached from Named types
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,9 @@ func hasEmptyTypeset(t Type) bool {
|
|||
// TODO(gri) should we include signatures or assert that they are not present?
|
||||
func isGeneric(t Type) bool {
|
||||
// A parameterized type is only generic if it doesn't have an instantiation already.
|
||||
if alias, _ := t.(*Alias); alias != nil && alias.tparams != nil && alias.targs == nil {
|
||||
return true
|
||||
}
|
||||
named := asNamed(t)
|
||||
return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,17 +99,41 @@ func (subst *subster) typ(typ Type) Type {
|
|||
// nothing to do
|
||||
|
||||
case *Alias:
|
||||
rhs := subst.typ(t.fromRHS)
|
||||
if rhs != t.fromRHS {
|
||||
// This branch cannot be reached because the RHS of an alias
|
||||
// may only contain type parameters of an enclosing function.
|
||||
// Such function bodies are never "instantiated" and thus
|
||||
// substitution is not called on locally declared alias types.
|
||||
// TODO(gri) adjust once parameterized aliases are supported
|
||||
panic("unreachable for unparameterized aliases")
|
||||
// return subst.check.newAlias(t.obj, rhs)
|
||||
// This code follows the code for *Named types closely.
|
||||
// TODO(gri) try to factor better
|
||||
orig := t.Origin()
|
||||
n := orig.TypeParams().Len()
|
||||
if n == 0 {
|
||||
return t // type is not parameterized
|
||||
}
|
||||
|
||||
// TODO(gri) do we need this for Alias types?
|
||||
var newTArgs []Type
|
||||
if t.TypeArgs().Len() != n {
|
||||
return Typ[Invalid] // error reported elsewhere
|
||||
}
|
||||
|
||||
// already instantiated
|
||||
// For each (existing) type argument targ, determine if it needs
|
||||
// to be substituted; i.e., if it is or contains a type parameter
|
||||
// that has a type argument for it.
|
||||
for i, targ := range t.TypeArgs().list() {
|
||||
new_targ := subst.typ(targ)
|
||||
if new_targ != targ {
|
||||
if newTArgs == nil {
|
||||
newTArgs = make([]Type, n)
|
||||
copy(newTArgs, t.TypeArgs().list())
|
||||
}
|
||||
newTArgs[i] = new_targ
|
||||
}
|
||||
}
|
||||
|
||||
if newTArgs == nil {
|
||||
return t // nothing to substitute
|
||||
}
|
||||
|
||||
return subst.check.newAliasInstance(subst.pos, t.orig, newTArgs, subst.ctxt)
|
||||
|
||||
case *Array:
|
||||
elem := subst.typOrNil(t.elem)
|
||||
if elem != t.elem {
|
||||
|
|
|
|||
|
|
@ -338,6 +338,10 @@ func (w *typeWriter) typ(typ Type) {
|
|||
|
||||
case *Alias:
|
||||
w.typeName(t.obj)
|
||||
if list := t.targs.list(); len(list) != 0 {
|
||||
// instantiated type
|
||||
w.typeList(list)
|
||||
}
|
||||
if w.ctxt != nil {
|
||||
// TODO(gri) do we need to print the alias type name, too?
|
||||
w.typ(Unalias(t.obj.typ))
|
||||
|
|
|
|||
|
|
@ -443,6 +443,10 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName)
|
|||
}()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
setDefType(def, res)
|
||||
}()
|
||||
|
||||
var cause string
|
||||
gtyp := check.genericType(ix.X, &cause)
|
||||
if cause != "" {
|
||||
|
|
@ -452,21 +456,23 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName)
|
|||
return gtyp // error already reported
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
targs := check.typeList(ix.Indices)
|
||||
if targs == nil {
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
if orig, _ := gtyp.(*Alias); orig != nil {
|
||||
return check.instance(ix.Pos(), orig, targs, nil, check.context())
|
||||
}
|
||||
|
||||
orig := asNamed(gtyp)
|
||||
if orig == nil {
|
||||
panic(fmt.Sprintf("%v: cannot instantiate %v", ix.Pos(), gtyp))
|
||||
}
|
||||
|
||||
// evaluate arguments
|
||||
targs := check.typeList(ix.Indices)
|
||||
if targs == nil {
|
||||
setDefType(def, Typ[Invalid]) // avoid errors later due to lazy instantiation
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// create the instance
|
||||
inst := asNamed(check.instance(ix.Pos(), orig, targs, nil, check.context()))
|
||||
setDefType(def, inst)
|
||||
|
||||
// orig.tparams may not be set up, so we need to do expansion later.
|
||||
check.later(func() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func hasDots(call *ast.CallExpr) bool { return call.Ellipsis.IsValid() }
|
|||
// dddErrPos returns the positioner for reporting an invalid ... use in a call.
|
||||
func dddErrPos(call *ast.CallExpr) positioner { return atPos(call.Ellipsis) }
|
||||
|
||||
// argErrPos returns positioner for reportign an invalid argument count.
|
||||
// argErrPos returns positioner for reporting an invalid argument count.
|
||||
func argErrPos(call *ast.CallExpr) positioner { return inNode(call, call.Rparen) }
|
||||
|
||||
// startPos returns the start position of node n.
|
||||
|
|
|
|||
|
|
@ -28,14 +28,20 @@ type _[P any, Q int] = RHS[P, Q]
|
|||
type _[P int | float64] = RHS[P, int]
|
||||
type _[P, Q any] = RHS[P, Q /* ERROR "Q does not satisfy ~int" */]
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// NOTE: The code below does now work yet.
|
||||
// TODO: Implement this.
|
||||
|
||||
// A generic type alias may be used like any other generic type.
|
||||
type A[P any] = RHS[P, int]
|
||||
|
||||
func _(a A /* ERROR "not a generic type" */ [string]) {
|
||||
func _(a A[string]) {
|
||||
a.p = "foo"
|
||||
a.q = 42
|
||||
}
|
||||
|
||||
// A generic alias may refer to another generic alias.
|
||||
type B[P any] = A[P]
|
||||
|
||||
func _(a B[string]) {
|
||||
a.p = "foo"
|
||||
a.q = 42
|
||||
// error messages print the instantiated alias type
|
||||
a.r /* ERROR "a.r undefined (type B[string] has no field or method r)" */ = 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue