// 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)
}
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
// 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) }
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
}
// 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
+}
}
// 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)
}
}
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 {
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)
{
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)
}