id    uint64    // unique id, for debugging only
        obj   *TypeName // corresponding type name
        index int       // type parameter index in source order, starting at 0
-       bound Type      // any type, but eventually an *Interface for correct programs (see TypeParam.iface)
+       bound Type      // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
 }
 
 // Obj returns the type name for the type parameter t.
        if obj.typ == nil {
                obj.typ = typ
        }
+       // iface may mutate typ.bound, so we must ensure that iface() is called
+       // at least once before the resulting TypeParam escapes.
+       if check != nil {
+               check.later(func() {
+                       typ.iface()
+               })
+       } else if constraint != nil {
+               typ.iface()
+       }
        return typ
 }
 
 }
 
 // SetConstraint sets the type constraint for t.
+//
+// SetConstraint should not be called concurrently, but once SetConstraint
+// returns the receiver t is safe for concurrent use.
 func (t *TypeParam) SetConstraint(bound Type) {
        if bound == nil {
                panic("nil constraint")
        }
        t.bound = bound
+       // iface may mutate t.bound (if bound is not an interface), so ensure that
+       // this is done before returning.
+       t.iface()
 }
 
 func (t *TypeParam) Underlying() Type {
 
        id    uint64    // unique id, for debugging only
        obj   *TypeName // corresponding type name
        index int       // type parameter index in source order, starting at 0
-       bound Type      // any type, but eventually an *Interface for correct programs (see TypeParam.iface)
+       bound Type      // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
 }
 
 // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
        if obj.typ == nil {
                obj.typ = typ
        }
+       // iface may mutate typ.bound, so we must ensure that iface() is called
+       // at least once before the resulting TypeParam escapes.
+       if check != nil {
+               check.later(func() {
+                       typ.iface()
+               })
+       } else if constraint != nil {
+               typ.iface()
+       }
        return typ
 }
 
 }
 
 // SetConstraint sets the type constraint for t.
+//
+// SetConstraint should not be called concurrently, but once SetConstraint
+// returns the receiver t is safe for concurrent use.
 func (t *TypeParam) SetConstraint(bound Type) {
        if bound == nil {
                panic("nil constraint")
        }
        t.bound = bound
+       // iface may mutate t.bound (if bound is not an interface), so ensure that
+       // this is done before returning.
+       t.iface()
 }
 
 func (t *TypeParam) Underlying() Type {
        }
 
        // 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