internal/aliases: add type parameters argument to NewAliases
Adds a type parameters argument to NewAliases and updates all
usage locations. Also adds a unit test that creates a type
parameterized alias.
Updates golang/go#68778
Change-Id: I5e3e76a5f597cf658faa9036319eded33eeb9286
Reviewed-on: https://go-review.googlesource.com/c/tools/+/607535
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/go/ssa/subst.go b/go/ssa/subst.go
index 4dcb871..6315158 100644
--- a/go/ssa/subst.go
+++ b/go/ssa/subst.go
@@ -365,19 +365,19 @@
rhs := subst.typ(aliases.Rhs(t))
// Create the fresh alias.
- obj := aliases.NewAlias(true, tname.Pos(), tname.Pkg(), tname.Name(), rhs)
- fresh := obj.Type()
- if fresh, ok := fresh.(*aliases.Alias); ok {
- // TODO: assume ok when aliases are always materialized (go1.27).
- aliases.SetTypeParams(fresh, newTParams)
- }
+ //
+ // Until 1.27, the result of aliases.NewAlias(...).Type() cannot guarantee it is a *types.Alias.
+ // However, as t is an *alias.Alias and t is well-typed, then aliases must have been enabled.
+ // Follow this decision, and always enable aliases here.
+ const enabled = true
+ obj := aliases.NewAlias(enabled, tname.Pos(), tname.Pkg(), tname.Name(), rhs, newTParams)
// Substitute into all of the constraints after they are created.
for i, ntp := range newTParams {
bound := tparams.At(i).Constraint()
ntp.SetConstraint(subst.typ(bound))
}
- return fresh
+ return obj.Type()
}
// t is declared within the function origin and has type arguments.
diff --git a/internal/aliases/aliases.go b/internal/aliases/aliases.go
index c24c2ee..f7798e3 100644
--- a/internal/aliases/aliases.go
+++ b/internal/aliases/aliases.go
@@ -22,11 +22,17 @@
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias.
-func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
+//
+// Precondition: enabled || len(tparams)==0.
+// If materialized aliases are disabled, there must not be any type parameters.
+func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
- newAlias(tname, rhs)
+ newAlias(tname, rhs, tparams)
return tname
}
+ if len(tparams) > 0 {
+ panic("cannot create an alias with type parameters when gotypesalias is not enabled")
+ }
return types.NewTypeName(pos, pkg, name, rhs)
}
diff --git a/internal/aliases/aliases_go121.go b/internal/aliases/aliases_go121.go
index 6652f7d..a775fcc 100644
--- a/internal/aliases/aliases_go121.go
+++ b/internal/aliases/aliases_go121.go
@@ -27,7 +27,9 @@
// Unalias returns the type t for go <=1.21.
func Unalias(t types.Type) types.Type { return t }
-func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
+func newAlias(name *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias {
+ panic("unreachable")
+}
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
diff --git a/internal/aliases/aliases_go122.go b/internal/aliases/aliases_go122.go
index 3ef1afe..31c159e 100644
--- a/internal/aliases/aliases_go122.go
+++ b/internal/aliases/aliases_go122.go
@@ -70,10 +70,9 @@
// newAlias is an internal alias around types.NewAlias.
// Direct usage is discouraged as the moment.
// Try to use NewAlias instead.
-func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
+func newAlias(tname *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias {
a := types.NewAlias(tname, rhs)
- // TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
- Unalias(a)
+ SetTypeParams(a, tparams)
return a
}
diff --git a/internal/aliases/aliases_test.go b/internal/aliases/aliases_test.go
index d27fd6d..d19afcc 100644
--- a/internal/aliases/aliases_test.go
+++ b/internal/aliases/aliases_test.go
@@ -24,7 +24,7 @@
// be an *aliases.Alias.
func TestNewAlias(t *testing.T) {
const source = `
- package P
+ package p
type Named int
`
@@ -35,7 +35,7 @@
}
var conf types.Config
- pkg, err := conf.Check("P", fset, []*ast.File{f}, nil)
+ pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
if err != nil {
t.Fatal(err)
}
@@ -47,15 +47,18 @@
}
for _, godebug := range []string{
- // "", // The default is in transition; suppress this case for now
+ // The default gotypesalias value follows the x/tools/go.mod version
+ // The go.mod is at 1.19 so the default is gotypesalias=0.
+ // "", // Use the default GODEBUG value.
"gotypesalias=0",
- "gotypesalias=1"} {
+ "gotypesalias=1",
+ } {
t.Run(godebug, func(t *testing.T) {
t.Setenv("GODEBUG", godebug)
enabled := aliases.Enabled()
- A := aliases.NewAlias(enabled, token.NoPos, pkg, "A", tv.Type)
+ A := aliases.NewAlias(enabled, token.NoPos, pkg, "A", tv.Type, nil)
if got, want := A.Name(), "A"; got != want {
t.Errorf("Expected A.Name()==%q. got %q", want, got)
}
@@ -75,3 +78,79 @@
})
}
}
+
+// TestNewAlias tests that alias.NewAlias can create a parameterized alias
+// A[T] of a type whose underlying and Unaliased type is *T. The test then
+// instantiates A[Named] and checks that the underlying and Unaliased type
+// of A[Named] is *Named.
+//
+// Requires gotypesalias GODEBUG and aliastypeparams GOEXPERIMENT.
+func TestNewParameterizedAlias(t *testing.T) {
+ testenv.NeedsGoExperiment(t, "aliastypeparams")
+
+ t.Setenv("GODEBUG", "gotypesalias=1") // needed until gotypesalias is removed (1.27).
+ enabled := aliases.Enabled()
+ if !enabled {
+ t.Fatal("Need materialized aliases enabled")
+ }
+
+ const source = `
+ package p
+
+ type Named int
+ `
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "hello.go", source, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf types.Config
+ pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // type A[T ~int] = *T
+ tparam := types.NewTypeParam(
+ types.NewTypeName(token.NoPos, pkg, "T", nil),
+ types.NewUnion([]*types.Term{types.NewTerm(true, types.Typ[types.Int])}),
+ )
+ ptrT := types.NewPointer(tparam)
+ A := aliases.NewAlias(enabled, token.NoPos, pkg, "A", ptrT, []*types.TypeParam{tparam})
+ if got, want := A.Name(), "A"; got != want {
+ t.Errorf("NewAlias: got %q, want %q", got, want)
+ }
+
+ if got, want := A.Type().Underlying(), ptrT; !types.Identical(got, want) {
+ t.Errorf("A.Type().Underlying (%q) is not identical to %q", got, want)
+ }
+ if got, want := aliases.Unalias(A.Type()), ptrT; !types.Identical(got, want) {
+ t.Errorf("Unalias(A)==%q is not identical to %q", got, want)
+ }
+
+ if _, ok := A.Type().(*aliases.Alias); !ok {
+ t.Errorf("Expected A.Type() to be a types.Alias(). got %q", A.Type())
+ }
+
+ pkg.Scope().Insert(A) // Add A to pkg so it is available to types.Eval.
+
+ named, ok := pkg.Scope().Lookup("Named").(*types.TypeName)
+ if !ok {
+ t.Fatalf("Failed to Lookup(%q) in package %s", "Named", pkg)
+ }
+ ptrNamed := types.NewPointer(named.Type())
+
+ const expr = `A[Named]`
+ tv, err := types.Eval(fset, pkg, 0, expr)
+ if err != nil {
+ t.Fatalf("Eval(%s) failed: %v", expr, err)
+ }
+
+ if got, want := tv.Type.Underlying(), ptrNamed; !types.Identical(got, want) {
+ t.Errorf("A[Named].Type().Underlying (%q) is not identical to %q", got, want)
+ }
+ if got, want := aliases.Unalias(tv.Type), ptrNamed; !types.Identical(got, want) {
+ t.Errorf("Unalias(A[Named])==%q is not identical to %q", got, want)
+ }
+}
diff --git a/internal/gcimporter/iimport.go b/internal/gcimporter/iimport.go
index 01c4023..1c8cf5a 100644
--- a/internal/gcimporter/iimport.go
+++ b/internal/gcimporter/iimport.go
@@ -569,7 +569,8 @@
// tparams := r.tparamList()
// alias.SetTypeParams(tparams)
// }
- r.declare(aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ))
+ var tparams []*types.TypeParam
+ r.declare(aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ, tparams))
case constTag:
typ, val := r.value()
diff --git a/internal/gcimporter/ureader_yes.go b/internal/gcimporter/ureader_yes.go
index 2c07706..50b4a37 100644
--- a/internal/gcimporter/ureader_yes.go
+++ b/internal/gcimporter/ureader_yes.go
@@ -526,7 +526,8 @@
case pkgbits.ObjAlias:
pos := r.pos()
typ := r.typ()
- declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ))
+ var tparams []*types.TypeParam // TODO(#68778): read type params once pkgbits.V2 is available.
+ declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams))
case pkgbits.ObjConst:
pos := r.pos()