From 9b4b4ae5859393993ec1a2359666a9c49d0d5b73 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Date: Thu, 25 Jul 2024 17:17:44 +0700
Subject: [PATCH] types2, go/types: fix instantiation of named type with
 generic alias

The typechecker is assuming that alias instances cannot be reached from
a named type. However, when type parameters on aliases are permited, it
can happen.

This CL changes the typechecker to propagate the correct named instance
is being expanded.

Updates #46477
Fixes #68580

Change-Id: Id0879021f4640c0fefe277701d5096c649413811
Reviewed-on: https://go-review.googlesource.com/c/go/+/601115
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
---
 src/cmd/compile/internal/types2/alias.go       |  4 ++--
 src/cmd/compile/internal/types2/instantiate.go |  8 +++++---
 src/cmd/compile/internal/types2/subst.go       |  2 +-
 src/go/types/alias.go                          |  4 ++--
 src/go/types/instantiate.go                    |  8 +++++---
 src/go/types/subst.go                          |  2 +-
 test/fixedbugs/issue68580.go                   | 15 +++++++++++++++
 7 files changed, 31 insertions(+), 12 deletions(-)
 create mode 100644 test/fixedbugs/issue68580.go

diff --git a/src/cmd/compile/internal/types2/alias.go b/src/cmd/compile/internal/types2/alias.go
index 5148d5db03..07f35b1854 100644
--- a/src/cmd/compile/internal/types2/alias.go
+++ b/src/cmd/compile/internal/types2/alias.go
@@ -134,10 +134,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
 // 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 {
+func (check *Checker) newAliasInstance(pos syntax.Pos, orig *Alias, targs []Type, expanding *Named, 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)
+	rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
 	res := check.newAlias(obj, rhs)
 	res.orig = orig
 	res.tparams = orig.tparams
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 72227ab122..308d1f550a 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -11,6 +11,7 @@ import (
 	"cmd/compile/internal/syntax"
 	"errors"
 	"fmt"
+	"internal/buildcfg"
 	. "internal/types/errors"
 )
 
@@ -126,8 +127,9 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
 		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
+		if !buildcfg.Experiment.AliasTypeParams {
+			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)
@@ -138,7 +140,7 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
 			return orig // nothing to do (minor optimization)
 		}
 
-		return check.newAliasInstance(pos, orig, targs, ctxt)
+		return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
 
 	case *Signature:
 		assert(expanding == nil) // function instances cannot be reached from Named types
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index 650ae846a6..7c4cd73250 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -115,7 +115,7 @@ func (subst *subster) typ(typ Type) Type {
 		// that has a type argument for it.
 		targs, updated := subst.typeList(t.TypeArgs().list())
 		if updated {
-			return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
+			return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
 		}
 
 	case *Array:
diff --git a/src/go/types/alias.go b/src/go/types/alias.go
index af43471a32..7adb3deb58 100644
--- a/src/go/types/alias.go
+++ b/src/go/types/alias.go
@@ -137,10 +137,10 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
 // 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 {
+func (check *Checker) newAliasInstance(pos token.Pos, orig *Alias, targs []Type, expanding *Named, 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)
+	rhs := check.subst(pos, orig.fromRHS, makeSubstMap(orig.TypeParams().list(), targs), expanding, ctxt)
 	res := check.newAlias(obj, rhs)
 	res.orig = orig
 	res.tparams = orig.tparams
diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go
index 7bec790b55..0435f2bf26 100644
--- a/src/go/types/instantiate.go
+++ b/src/go/types/instantiate.go
@@ -14,6 +14,7 @@ import (
 	"errors"
 	"fmt"
 	"go/token"
+	"internal/buildcfg"
 	. "internal/types/errors"
 )
 
@@ -129,8 +130,9 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
 		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
+		if !buildcfg.Experiment.AliasTypeParams {
+			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)
@@ -141,7 +143,7 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
 			return orig // nothing to do (minor optimization)
 		}
 
-		return check.newAliasInstance(pos, orig, targs, ctxt)
+		return check.newAliasInstance(pos, orig, targs, expanding, ctxt)
 
 	case *Signature:
 		assert(expanding == nil) // function instances cannot be reached from Named types
diff --git a/src/go/types/subst.go b/src/go/types/subst.go
index 5ad2ff61eb..6be106d3aa 100644
--- a/src/go/types/subst.go
+++ b/src/go/types/subst.go
@@ -118,7 +118,7 @@ func (subst *subster) typ(typ Type) Type {
 		// that has a type argument for it.
 		targs, updated := subst.typeList(t.TypeArgs().list())
 		if updated {
-			return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.ctxt)
+			return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt)
 		}
 
 	case *Array:
diff --git a/test/fixedbugs/issue68580.go b/test/fixedbugs/issue68580.go
new file mode 100644
index 0000000000..b60a7447aa
--- /dev/null
+++ b/test/fixedbugs/issue68580.go
@@ -0,0 +1,15 @@
+// compile -goexperiment aliastypeparams
+
+// Copyright 2024 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
+
+type A[P any] = struct{ _ P }
+
+type N[P any] A[P]
+
+func f[P any](N[P]) {}
+
+var _ = f[int]