Skip to content

Commit

Permalink
go/types, types2: check that alias type arguments satisfy constraints
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
griesemer authored and Robert Griesemer committed Sep 24, 2024
1 parent 68bcef7 commit 5a1de4e
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 60 deletions.
1 change: 1 addition & 0 deletions src/cmd/compile/internal/types2/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/cmd/compile/internal/types2/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/types2/instantiate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 21 additions & 25 deletions src/cmd/compile/internal/types2/typexpr.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,57 +450,53 @@ 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)
if targs == nil {
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) {
pos = syntax.StartPos(xlist[i])
}
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
}
Expand Down
1 change: 1 addition & 0 deletions src/go/types/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/go/types/context.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/go/types/instantiate.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 21 additions & 25 deletions src/go/types/typexpr.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,57 +440,53 @@ 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)
if targs == nil {
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) {
pos = ix.Indices[i].Pos()
}
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
}
Expand Down
11 changes: 11 additions & 0 deletions src/internal/types/testdata/fixedbugs/issue69576.go
Original file line number Diff line number Diff line change
@@ -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)" */]

0 comments on commit 5a1de4e

Please sign in to comment.