Skip to content

Commit

Permalink
cmd/compile/internal/types2: mark implicit interfaces as such
Browse files Browse the repository at this point in the history
Provide an accessor for clients, and don't print the interface
around implicitly wrapped embedded types.

For #48424.

Change-Id: Ib2c76315508fc749ea4337d52e13d17de80e04da
Reviewed-on: https://go-review.googlesource.com/c/go/+/353396
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
  • Loading branch information
griesemer committed Oct 1, 2021
1 parent 952df98 commit 3357624
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 9 deletions.
11 changes: 6 additions & 5 deletions src/cmd/compile/internal/types2/decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,11 +652,12 @@ func (check *Checker) bound(x syntax.Expr) Type {
// embed it in an implicit interface so that only interface type-checking
// needs to take care of such type expressions.
if op, _ := x.(*syntax.Operation); op != nil && (op.Op == syntax.Tilde || op.Op == syntax.Or) {
// TODO(gri) Should mark this interface as "implicit" somehow
// (and propagate the info to types2.Interface) so
// that we can elide the interface again in error
// messages. Could use a sentinel name for the field.
x = &syntax.InterfaceType{MethodList: []*syntax.Field{{Type: x}}}
t := check.typ(&syntax.InterfaceType{MethodList: []*syntax.Field{{Type: x}}})
// mark t as implicit interface if all went well
if t, _ := t.(*Interface); t != nil {
t.implicit = true
}
return t
}
return check.typ(x)
}
Expand Down
5 changes: 4 additions & 1 deletion src/cmd/compile/internal/types2/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Interface struct {
methods []*Func // ordered list of explicitly declared methods
embeddeds []Type // ordered list of explicitly embedded elements
embedPos *[]syntax.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space
implicit bool // interface is wrapper for type set literal (non-interface T, ~T, or A|B)
complete bool // indicates that all fields (except for tset) are set up

tset *_TypeSet // type set described by this interface, computed lazily
Expand Down Expand Up @@ -82,6 +83,9 @@ func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
// IsMethodSet reports whether the interface t is fully described by its method set.
func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }

// IsImplicit reports whether the interface t is a wrapper for a type set literal.
func (t *Interface) IsImplicit() bool { return t.implicit }

func (t *Interface) Underlying() Type { return t }
func (t *Interface) String() string { return TypeString(t, nil) }

Expand All @@ -102,7 +106,6 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType

for _, f := range iface.MethodList {
if f.Name == nil {
// We have an embedded type; possibly a union of types.
addEmbedded(posFor(f.Type), parseUnion(check, flattenUnion(nil, f.Type)))
continue
}
Expand Down
10 changes: 10 additions & 0 deletions src/cmd/compile/internal/types2/testdata/examples/typesets.go2
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@ func _() *int {
// A type parameter may not be embedded in an interface;
// so it can also not be used as a constraint.
func _[A any, B A /* ERROR cannot use a type parameter as constraint */ ]() {}

// Error messages refer to the type constraint as it appears in the source.
// (No implicit interface should be exposed.)
func _[T string](x T) T {
return x /* ERROR constrained by string */ * x
}

func _[T int|string](x T) T {
return x /* ERROR constrained by int|string */ * x
}
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/types2/typeparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ func (t *TypeParam) iface() *Interface {
}

// If we don't have an interface, wrap constraint into an implicit interface.
// TODO(gri) mark it as implicit - see comment in Checker.bound
if ityp == nil {
ityp = NewInterfaceType(nil, []Type{bound})
ityp.implicit = true
t.bound = ityp // update t.bound for next time (optimization)
}

Expand Down
9 changes: 9 additions & 0 deletions src/cmd/compile/internal/types2/typestring.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ func (w *typeWriter) typ(typ Type) {
}

case *Interface:
if t.implicit {
if len(t.methods) == 0 && len(t.embeddeds) == 1 {
w.typ(t.embeddeds[0])
break
}
// Something's wrong with the implicit interface.
// Print it as such and continue.
w.string("/* implicit */ ")
}
w.string("interface{")
first := true
for _, m := range t.methods {
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/compile/internal/types2/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func defPredeclaredTypes() {
res := NewVar(nopos, nil, "", Typ[String])
sig := NewSignatureType(nil, nil, nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)
ityp := &Interface{nil, obj, []*Func{err}, nil, nil, true, nil}
ityp := &Interface{nil, obj, []*Func{err}, nil, nil, false, true, nil}
computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset
typ := NewNamed(obj, ityp, nil)
sig.recv = NewVar(nopos, nil, "", typ)
Expand All @@ -99,7 +99,7 @@ func defPredeclaredTypes() {
{
obj := NewTypeName(nopos, nil, "comparable", nil)
obj.setColor(black)
ityp := &Interface{nil, obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}}
ityp := &Interface{nil, obj, nil, nil, nil, false, true, &_TypeSet{true, nil, allTermlist}}
NewNamed(obj, ityp, nil)
def(obj)
}
Expand Down

0 comments on commit 3357624

Please sign in to comment.