]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: ensure that constructed type parameters are immutable
authorRobert Findley <rfindley@google.com>
Mon, 29 Nov 2021 19:52:54 +0000 (14:52 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 29 Nov 2021 22:25:04 +0000 (22:25 +0000)
TypeParam.iface may mutate TypeParam.bound in the event that the type
parameter bound is not an interface.

Ensure that iface() is called before the type-checking pass returns, and
before NewTypeParam or TypeParam.SetConstraint exits.

Fixes #49788

Change-Id: I72279acf5f0223161671c04887bc2c3df4158927
Reviewed-on: https://go-review.googlesource.com/c/go/+/367614
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/types2/typeparam.go
src/go/types/typeparam.go

index 8dd04ff408ffa3e6931a3d0e6a0b8b5d501c07a5..e32063a0af1dc85dbd9d00ffc53bacbfa6109e50 100644 (file)
@@ -21,7 +21,7 @@ type TypeParam struct {
        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.
@@ -47,6 +47,15 @@ func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
        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
 }
 
@@ -62,11 +71,17 @@ func (t *TypeParam) Constraint() Type {
 }
 
 // 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 {
index 7cce1f7e357d55d18d958b0234bb2f92c7c379df..03ba9be55c564d669ae8ba33bd0c2add2bfb5415 100644 (file)
@@ -24,7 +24,7 @@ type TypeParam struct {
        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
@@ -47,6 +47,15 @@ func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
        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
 }
 
@@ -65,11 +74,17 @@ func (t *TypeParam) Constraint() Type {
 }
 
 // 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 {
@@ -104,7 +119,6 @@ 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