[dev.typeparams] cmd/compile: get type aliases working with generic types

Generic types can the source type of a type alias, so modify g.typ0() to
be able to deal with base generic types.

Added test aliasimp.go that tests aliasing of local generic types and
imported generic types.

Change-Id: I1c398193819d47a36b014cc1f9bb55107e9a565b
Reviewed-on: https://go-review.googlesource.com/c/go/+/322194
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Dan Scales 2021-05-24 14:15:48 -07:00
parent 95748d1b74
commit b7f7d1cd7b
4 changed files with 76 additions and 23 deletions

View file

@ -91,50 +91,49 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
case *types2.Basic: case *types2.Basic:
return g.basic(typ) return g.basic(typ)
case *types2.Named: case *types2.Named:
if typ.TParams() != nil { // If tparams is set, but targs is not, typ is a base generic
// type. typ is appearing as part of the source type of an alias,
// since that is the only use of a generic type that doesn't
// involve instantiation. We just translate the named type in the
// normal way below using g.obj().
if typ.TParams() != nil && typ.TArgs() != nil {
// typ is an instantiation of a defined (named) generic type. // typ is an instantiation of a defined (named) generic type.
// This instantiation should also be a defined (named) type. // This instantiation should also be a defined (named) type.
// types2 gives us the substituted type in t.Underlying() // types2 gives us the substituted type in t.Underlying()
// The substituted type may or may not still have type // The substituted type may or may not still have type
// params. We might, for example, be substituting one type // params. We might, for example, be substituting one type
// param for another type param. // param for another type param.
//
if typ.TArgs() == nil { // When converted to types.Type, typ has a unique name,
base.Fatalf("In typ0, Targs should be set if TParams is set") // based on the names of the type arguments.
}
// When converted to types.Type, typ must have a name,
// based on the names of the type arguments. We need a
// name to deal with recursive generic types (and it also
// looks better when printing types).
instName := instTypeName2(typ.Obj().Name(), typ.TArgs()) instName := instTypeName2(typ.Obj().Name(), typ.TArgs())
s := g.pkg(typ.Obj().Pkg()).Lookup(instName) s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
if s.Def != nil { if s.Def != nil {
// We have already encountered this instantiation, // We have already encountered this instantiation.
// so use the type we previously created, since there // Use the type we previously created, since there
// must be exactly one instance of a defined type. // must be exactly one instance of a defined type.
return s.Def.Type() return s.Def.Type()
} }
// Create a forwarding type first and put it in the g.typs // Create a forwarding type first and put it in the g.typs
// map, in order to deal with recursive generic types. // map, in order to deal with recursive generic types
// Fully set up the extra ntyp information (Def, RParams, // (including via method signatures).. Set up the extra
// which may set HasTParam) before translating the // ntyp information (Def, RParams, which may set
// underlying type itself, so we handle recursion // HasTParam) before translating the underlying type
// correctly, including via method signatures. // itself, so we handle recursion correctly.
ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s) ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
g.typs[typ] = ntyp g.typs[typ] = ntyp
// If ntyp still has type params, then we must be // If ntyp still has type params, then we must be
// referencing something like 'value[T2]', as when // referencing something like 'value[T2]', as when
// specifying the generic receiver of a method, // specifying the generic receiver of a method, where
// where value was defined as "type value[T any] // value was defined as "type value[T any] ...". Save the
// ...". Save the type args, which will now be the // type args, which will now be the new typeparams of the
// new type of the current type. // current type.
// //
// If ntyp does not have type params, we are saving the // If ntyp does not have type params, we are saving the
// concrete types used to instantiate this type. We'll use // non-generic types used to instantiate this type. We'll
// these when instantiating the methods of the // use these when instantiating the methods of the
// instantiated type. // instantiated type.
rparams := make([]*types.Type, len(typ.TArgs())) rparams := make([]*types.Type, len(typ.TArgs()))
for i, targ := range typ.TArgs() { for i, targ := range typ.TArgs() {

View file

@ -0,0 +1,9 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package a
type Rimp[T any] struct {
F T
}

View file

@ -0,0 +1,38 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "a"
type R[T any] struct {
F T
}
type S = R
type Sint = R[int]
type Simp = a.Rimp
type SimpString Simp[string]
func main() {
var s S[int]
if s.F != 0 {
panic(s.F)
}
var s2 Sint
if s2.F != 0 {
panic(s2.F)
}
var s3 Simp[string]
if s3.F != "" {
panic(s3.F)
}
var s4 SimpString
if s4.F != "" {
panic(s4.F)
}
}

View file

@ -0,0 +1,7 @@
// rundir -G=3
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ignored