From 186f375ecfdd0f9eae109464a93bb0ba8c993f45 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Mon, 29 Nov 2021 14:52:54 -0500 Subject: [PATCH] go/types: ensure that constructed type parameters are immutable 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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/types2/typeparam.go | 17 ++++++++++++++++- src/go/types/typeparam.go | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index 8dd04ff408..e32063a0af 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -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 { diff --git a/src/go/types/typeparam.go b/src/go/types/typeparam.go index 7cce1f7e35..03ba9be55c 100644 --- a/src/go/types/typeparam.go +++ b/src/go/types/typeparam.go @@ -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 -- 2.50.0