]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: check that alias type arguments satisfy constraints
authorRobert Griesemer <gri@golang.org>
Mon, 23 Sep 2024 20:02:16 +0000 (13:02 -0700)
committerRobert Griesemer <gri@google.com>
Tue, 24 Sep 2024 20:28:36 +0000 (20:28 +0000)
Fixes #69576.

Change-Id: I8fc077970276977dd89fc2dd3867f2765d52e54e
Reviewed-on: https://go-review.googlesource.com/c/go/+/615275
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Tim King <taking@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/context.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/typexpr.go
src/go/types/call.go
src/go/types/context.go
src/go/types/instantiate.go
src/go/types/typexpr.go
src/internal/types/testdata/fixedbugs/issue69576.go [new file with mode: 0644]

index 33bea5e9ffdf892ebad2c84387da04112621eaa8..551b1c1a908dd699172de440095728599083b9e5 100644 (file)
@@ -150,6 +150,7 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ
        // verify instantiation lazily (was go.dev/issue/50450)
        check.later(func() {
                tparams := typ.TypeParams().list()
+               // check type constraints
                if i, err := check.verify(pos, tparams, targs, check.context()); err != nil {
                        // best position for error reporting
                        pos := pos
index 772312463e5fbae6007b6fb70a1791998760198b..23efd065867021c3c131054c001044938a83d727 100644 (file)
@@ -101,10 +101,10 @@ func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type {
        return nil
 }
 
-// update de-duplicates n against previously seen types with the hash h.  If an
-// identical type is found with the type hash h, the previously seen type is
-// returned. Otherwise, n is returned, and recorded in the Context for the hash
-// h.
+// update de-duplicates inst against previously seen types with the hash h.
+// If an identical type is found with the type hash h, the previously seen
+// type is returned. Otherwise, inst is returned, and recorded in the Context
+// for the hash h.
 func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
        assert(inst != nil)
 
index 732d076ec3a4a8484b8dca230cd467f654e91ad4..df6aaf1ffab75372b0c7fbb308f9cc5ad808aa95 100644 (file)
@@ -140,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, expanding, ctxt)
+               res = check.newAliasInstance(pos, orig, targs, expanding, ctxt)
 
        case *Signature:
                assert(expanding == nil) // function instances cannot be reached from Named types
index b917a86c10e9944c4de172e10ab7b5f8c4e010d9..265f5b251274b74941e5653b9daac4ac6a0e97ff 100644 (file)
@@ -450,13 +450,18 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
        }()
 
        var cause string
-       gtyp := check.genericType(x, &cause)
+       typ := check.genericType(x, &cause)
        if cause != "" {
                check.errorf(x, NotAGenericType, invalidOp+"%s%s (%s)", x, xlist, cause)
        }
-       if !isValid(gtyp) {
-               return gtyp // error already reported
+       if !isValid(typ) {
+               return typ // error already reported
        }
+       // typ must be a generic Alias or Named type (but not a *Signature)
+       if _, ok := typ.(*Signature); ok {
+               panic("unexpected generic signature")
+       }
+       gtyp := typ.(genericType)
 
        // evaluate arguments
        targs := check.typeList(xlist)
@@ -464,27 +469,23 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
                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))
-       }
-
-       // create the instance
-       inst := asNamed(check.instance(x.Pos(), orig, targs, nil, check.context()))
+       // create instance
+       // The instance is not generic anymore as it has type arguments, but it still
+       // satisfies the genericType interface because it has type parameters, too.
+       inst := check.instance(x.Pos(), gtyp, targs, nil, check.context()).(genericType)
 
-       // orig.tparams may not be set up, so we need to do expansion later.
+       // For Named types, orig.tparams may not be set up, so we need to do expansion later.
        check.later(func() {
                // This is an instance from the source, not from recursive substitution,
                // and so it must be resolved during type-checking so that we can report
                // errors.
-               check.recordInstance(x, inst.TypeArgs().list(), inst)
+               check.recordInstance(x, targs, inst)
 
-               if check.validateTArgLen(x.Pos(), inst.obj.name, inst.TypeParams().Len(), inst.TypeArgs().Len()) {
-                       if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil {
+               name := inst.(interface{ Obj() *TypeName }).Obj().name
+               tparams := inst.TypeParams().list()
+               if check.validateTArgLen(x.Pos(), name, len(tparams), len(targs)) {
+                       // check type constraints
+                       if i, err := check.verify(x.Pos(), inst.TypeParams().list(), targs, check.context()); err != nil {
                                // best position for error reporting
                                pos := x.Pos()
                                if i < len(xlist) {
@@ -492,15 +493,10 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
                                }
                                check.softErrorf(pos, InvalidTypeArg, "%s", err)
                        } else {
-                               check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), xlist)
+                               check.mono.recordInstance(check.pkg, x.Pos(), tparams, targs, xlist)
                        }
                }
-
-               // TODO(rfindley): remove this call: we don't need to call validType here,
-               // as cycles can only occur for types used inside a Named type declaration,
-               // and so it suffices to call validType from declared types.
-               check.validType(inst)
-       }).describef(x, "resolve instance %s", inst)
+       }).describef(x, "verify instantiation %s", inst)
 
        return inst
 }
index d1324d425ae82a59ed803e760b67dfc46e9768f8..60a5b2d972053f71458432f6f3c4b4bab9d2784c 100644 (file)
@@ -152,6 +152,7 @@ func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Si
        // verify instantiation lazily (was go.dev/issue/50450)
        check.later(func() {
                tparams := typ.TypeParams().list()
+               // check type constraints
                if i, err := check.verify(pos, tparams, targs, check.context()); err != nil {
                        // best position for error reporting
                        pos := pos
index 5fe336a82fb8e2df6ecfbf308cacc48e43aaa09d..84f2468a8582a8cc8990071e5bf6c11c607f9208 100644 (file)
@@ -104,10 +104,10 @@ func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type {
        return nil
 }
 
-// update de-duplicates n against previously seen types with the hash h.  If an
-// identical type is found with the type hash h, the previously seen type is
-// returned. Otherwise, n is returned, and recorded in the Context for the hash
-// h.
+// update de-duplicates inst against previously seen types with the hash h.
+// If an identical type is found with the type hash h, the previously seen
+// type is returned. Otherwise, inst is returned, and recorded in the Context
+// for the hash h.
 func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
        assert(inst != nil)
 
index cef495314e735d8f553cf5f238ba26074ef460ce..b6e5b1f34e07a78ff952d1ffd71a7bd6ba4aed6d 100644 (file)
@@ -143,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, expanding, ctxt)
+               res = check.newAliasInstance(pos, orig, targs, expanding, ctxt)
 
        case *Signature:
                assert(expanding == nil) // function instances cannot be reached from Named types
index 926013b16cd482a0f48d07a6cc7d4a31cb3b1307..aa2d782563d5b8b7d58713d8602acc88c0fc4505 100644 (file)
@@ -440,13 +440,18 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName)
        }()
 
        var cause string
-       gtyp := check.genericType(ix.X, &cause)
+       typ := check.genericType(ix.X, &cause)
        if cause != "" {
                check.errorf(ix.Orig, NotAGenericType, invalidOp+"%s (%s)", ix.Orig, cause)
        }
-       if !isValid(gtyp) {
-               return gtyp // error already reported
+       if !isValid(typ) {
+               return typ // error already reported
        }
+       // typ must be a generic Alias or Named type (but not a *Signature)
+       if _, ok := typ.(*Signature); ok {
+               panic("unexpected generic signature")
+       }
+       gtyp := typ.(genericType)
 
        // evaluate arguments
        targs := check.typeList(ix.Indices)
@@ -454,27 +459,23 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName)
                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))
-       }
-
-       // create the instance
-       inst := asNamed(check.instance(ix.Pos(), orig, targs, nil, check.context()))
+       // create instance
+       // The instance is not generic anymore as it has type arguments, but it still
+       // satisfies the genericType interface because it has type parameters, too.
+       inst := check.instance(ix.Pos(), gtyp, targs, nil, check.context()).(genericType)
 
-       // orig.tparams may not be set up, so we need to do expansion later.
+       // For Named types, orig.tparams may not be set up, so we need to do expansion later.
        check.later(func() {
                // This is an instance from the source, not from recursive substitution,
                // and so it must be resolved during type-checking so that we can report
                // errors.
-               check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
+               check.recordInstance(ix.Orig, targs, inst)
 
-               if check.validateTArgLen(ix.Pos(), inst.obj.name, inst.TypeParams().Len(), inst.TypeArgs().Len()) {
-                       if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil {
+               name := inst.(interface{ Obj() *TypeName }).Obj().name
+               tparams := inst.TypeParams().list()
+               if check.validateTArgLen(ix.Pos(), name, len(tparams), len(targs)) {
+                       // check type constraints
+                       if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), targs, check.context()); err != nil {
                                // best position for error reporting
                                pos := ix.Pos()
                                if i < len(ix.Indices) {
@@ -482,15 +483,10 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName)
                                }
                                check.softErrorf(atPos(pos), InvalidTypeArg, "%v", err)
                        } else {
-                               check.mono.recordInstance(check.pkg, ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), ix.Indices)
+                               check.mono.recordInstance(check.pkg, ix.Pos(), tparams, targs, ix.Indices)
                        }
                }
-
-               // TODO(rfindley): remove this call: we don't need to call validType here,
-               // as cycles can only occur for types used inside a Named type declaration,
-               // and so it suffices to call validType from declared types.
-               check.validType(inst)
-       }).describef(ix, "resolve instance %s", inst)
+       }).describef(ix, "verify instantiation %s", inst)
 
        return inst
 }
diff --git a/src/internal/types/testdata/fixedbugs/issue69576.go b/src/internal/types/testdata/fixedbugs/issue69576.go
new file mode 100644 (file)
index 0000000..97e03df
--- /dev/null
@@ -0,0 +1,11 @@
+// -goexperiment=aliastypeparams -gotypesalias=1
+
+// 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 p
+
+type A[P int] = struct{}
+
+var _ A[string /* ERROR "string does not satisfy int (string missing in int)" */]