From b7f7d1cd7b3d965ec25d365b3e5057ef3278c729 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 24 May 2021 14:15:48 -0700 Subject: [PATCH] [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 Trust: Robert Griesemer Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/noder/types.go | 45 ++++++++++++------------- test/typeparam/aliasimp.dir/a.go | 9 +++++ test/typeparam/aliasimp.dir/main.go | 38 +++++++++++++++++++++ test/typeparam/aliasimp.go | 7 ++++ 4 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 test/typeparam/aliasimp.dir/a.go create mode 100644 test/typeparam/aliasimp.dir/main.go create mode 100644 test/typeparam/aliasimp.go diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index c6e97d4206..ae10e03a24 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -91,50 +91,49 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { case *types2.Basic: return g.basic(typ) 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. // This instantiation should also be a defined (named) type. // types2 gives us the substituted type in t.Underlying() // The substituted type may or may not still have type // params. We might, for example, be substituting one type // param for another type param. - - if typ.TArgs() == nil { - base.Fatalf("In typ0, Targs should be set if TParams is set") - } - - // 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). + // + // When converted to types.Type, typ has a unique name, + // based on the names of the type arguments. instName := instTypeName2(typ.Obj().Name(), typ.TArgs()) s := g.pkg(typ.Obj().Pkg()).Lookup(instName) if s.Def != nil { - // We have already encountered this instantiation, - // so use the type we previously created, since there + // We have already encountered this instantiation. + // Use the type we previously created, since there // must be exactly one instance of a defined type. return s.Def.Type() } // Create a forwarding type first and put it in the g.typs - // map, in order to deal with recursive generic types. - // Fully set up the extra ntyp information (Def, RParams, - // which may set HasTParam) before translating the - // underlying type itself, so we handle recursion - // correctly, including via method signatures. + // map, in order to deal with recursive generic types + // (including via method signatures).. Set up the extra + // ntyp information (Def, RParams, which may set + // HasTParam) before translating the underlying type + // itself, so we handle recursion correctly. ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s) g.typs[typ] = ntyp // If ntyp still has type params, then we must be // referencing something like 'value[T2]', as when - // specifying the generic receiver of a method, - // where value was defined as "type value[T any] - // ...". Save the type args, which will now be the - // new type of the current type. + // specifying the generic receiver of a method, where + // value was defined as "type value[T any] ...". Save the + // type args, which will now be the new typeparams of the + // current type. // // If ntyp does not have type params, we are saving the - // concrete types used to instantiate this type. We'll use - // these when instantiating the methods of the + // non-generic types used to instantiate this type. We'll + // use these when instantiating the methods of the // instantiated type. rparams := make([]*types.Type, len(typ.TArgs())) for i, targ := range typ.TArgs() { diff --git a/test/typeparam/aliasimp.dir/a.go b/test/typeparam/aliasimp.dir/a.go new file mode 100644 index 0000000000..3fac4aac98 --- /dev/null +++ b/test/typeparam/aliasimp.dir/a.go @@ -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 +} diff --git a/test/typeparam/aliasimp.dir/main.go b/test/typeparam/aliasimp.dir/main.go new file mode 100644 index 0000000000..6638fa9454 --- /dev/null +++ b/test/typeparam/aliasimp.dir/main.go @@ -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) + } +} diff --git a/test/typeparam/aliasimp.go b/test/typeparam/aliasimp.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/aliasimp.go @@ -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