aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-09-25 13:33:58 -0400
committerCherry Mui <cherryyz@google.com>2025-09-25 13:33:59 -0400
commita693ae1e9aebac896f6634583dbdd1cd319f3983 (patch)
tree44ef04e84afe5ef8652222c5500ab6c779d09650 /src/cmd/compile
parent5a78e1a4a1c79185e86b5c18efffba2a9b9d3739 (diff)
parentd70ad4e740e24b4b76961c4b56d698fa23668aa2 (diff)
downloadgo-a693ae1e9aebac896f6634583dbdd1cd319f3983.tar.xz
[dev.simd] all: merge master (d70ad4e) into dev.simd
Conflicts: - src/cmd/compile/internal/types2/stdlib_test.go - src/go/types/stdlib_test.go Merge List: + 2025-09-25 d70ad4e740 sync/atomic: correct Uintptr.Or return doc + 2025-09-25 d7abfe4f0d runtime: acquire/release C TSAN lock when calling cgo symbolizer/tracebacker + 2025-09-25 393d91aea0 cmd/fix: remove all functionality + 2025-09-25 6dceff8bad cmd/link: handle -w flag in external linking mode + 2025-09-25 76d088eb74 cmd/internal/obj/riscv: remove ACFLWSP/ACFSWSP and ACFLW/ACFSW + 2025-09-25 5225e9dc49 doc/next: document new image/jpeg DCT in release notes + 2025-09-25 81a83bba21 cmd: update x/tools@4df13e3 + 2025-09-25 6b32c613ca go/types: make typeset return an iterator + 2025-09-25 fbba930271 image/jpeg: replace fdct.go and idct.go with new implementation in dct.go + 2025-09-25 92e093467f image/jpeg: correct and test reference slowFDCT and slowIDCT + 2025-09-25 27c7bbc51c image/jpeg: prepare for new FDCT/IDCT implementations + 2025-09-24 f15cd63ec4 cmd/compile: don't rely on loop info when there are irreducible loops + 2025-09-24 371c1d2fcb cmd/internal/obj/riscv: add support for vector unit-stride fault-only-first load instructions + 2025-09-23 411c250d64 runtime: add specialized malloc functions for sizes up to 512 bytes + 2025-09-23 d7a38adf4c runtime: eliminate global span queue [green tea] + 2025-09-23 7bc1935db5 cmd/compile/internal: support new(expr) + 2025-09-23 eb78f13c9f doc/go_spec.html: document new(expr) + 2025-09-23 74cc463f9e go/token: add TestRemovedFileFileReturnsNil test + 2025-09-23 902dc27ae9 go/token: clear cache after grabbing the mutex in RemoveFile + 2025-09-23 a13d085a5b cmd/cgo: don't hardcode section name in TestNumberOfExportedFunctions + 2025-09-23 61bf26a9ee cmd/link: fix Macho-O X86_64_RELOC_SUBTRACTOR in internal linking + 2025-09-23 4b787c8c2b reflect: remove stale comment in unpackEface + 2025-09-23 3df27cd21a cmd/compile: fix typo in comment + 2025-09-23 684e8d3363 reflect: allocate memory in TypeAssert[I] only when the assertion succeeds + 2025-09-23 a5866ebe40 cmd/compile: prevent shapifying of pointer shape type + 2025-09-23 a27261c42f go/types,types2: allow new(expr) + 2025-09-23 e93f439ac4 runtime/cgo: retry when CreateThread fails with ERROR_ACCESS_DENIED + 2025-09-23 69e74b0aac runtime: deduplicate pMask resize code + 2025-09-23 fde10c4ce7 runtime: split gcMarkWorkAvailable into two separate conditions + 2025-09-23 5d040df092 runtime: use scan kernels in scanSpan [green tea] + 2025-09-23 7e0251bf58 runtime: don't report non-blocked goroutines as "(durable)" in stacks + 2025-09-23 22ac328856 cmd/link: make -w behavior consistent on Windows Change-Id: Id76b5a30a3b6f6669437f97e3320c9bca65a1e96
Diffstat (limited to 'src/cmd/compile')
-rw-r--r--src/cmd/compile/internal/ir/node.go2
-rw-r--r--src/cmd/compile/internal/ir/type.go4
-rw-r--r--src/cmd/compile/internal/noder/doc.go6
-rw-r--r--src/cmd/compile/internal/noder/reader.go59
-rw-r--r--src/cmd/compile/internal/noder/writer.go8
-rw-r--r--src/cmd/compile/internal/ssa/tighten.go27
-rw-r--r--src/cmd/compile/internal/types2/builtins.go74
-rw-r--r--src/cmd/compile/internal/types2/index.go12
-rw-r--r--src/cmd/compile/internal/types2/signature.go7
-rw-r--r--src/cmd/compile/internal/types2/stdlib_test.go1
-rw-r--r--src/cmd/compile/internal/types2/typeparam.go10
-rw-r--r--src/cmd/compile/internal/types2/typeset.go16
-rw-r--r--src/cmd/compile/internal/types2/under.go57
-rw-r--r--src/cmd/compile/internal/types2/version.go1
-rw-r--r--src/cmd/compile/testdata/script/issue75461.txt78
15 files changed, 223 insertions, 139 deletions
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 003ec15de1..8c61bb6ed5 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -215,7 +215,7 @@ const (
ORSH // X >> Y
OAND // X & Y
OANDNOT // X &^ Y
- ONEW // new(X); corresponds to calls to new in source code
+ ONEW // new(X); corresponds to calls to new(T) in source code
ONOT // !X
OBITNOT // ^X
OPLUS // +X
diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go
index 6daca856a6..0f44cf8d04 100644
--- a/src/cmd/compile/internal/ir/type.go
+++ b/src/cmd/compile/internal/ir/type.go
@@ -42,6 +42,10 @@ func TypeNode(t *types.Type) Node {
// A DynamicType represents a type expression whose exact type must be
// computed dynamically.
+//
+// TODO(adonovan): I think "dynamic" is a misnomer here; it's really a
+// type with free type parameters that needs to be instantiated to obtain
+// a ground type for which an rtype can exist.
type DynamicType struct {
miniExpr
diff --git a/src/cmd/compile/internal/noder/doc.go b/src/cmd/compile/internal/noder/doc.go
index a5d5533168..8eb67e92f0 100644
--- a/src/cmd/compile/internal/noder/doc.go
+++ b/src/cmd/compile/internal/noder/doc.go
@@ -87,7 +87,7 @@ constant for file bases and hence not encoded.
[ Sync ]
StringRef // the (absolute) file name for the base
Bool // true if a file base, else a line base
- // The below is ommitted for file bases.
+ // The below is omitted for file bases.
[ Pos
Uint64 // line
Uint64 ] // column
@@ -99,7 +99,7 @@ without a PosBase have no line or column.
Pos = [ Sync ]
Bool // true if the position has a base
- // The below is ommitted if the position has no base.
+ // The below is omitted if the position has no base.
[ Ref[PosBase]
Uint64 // line
Uint64 ] // column
@@ -125,7 +125,7 @@ packages. The below package paths have special meaning.
Pkg = RefTable
[ Sync ]
StringRef // path
- // The below is ommitted for the special package paths
+ // The below is omitted for the special package paths
// "builtin" and "unsafe".
[ StringRef // name
Imports ]
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 3cbc7989a7..ca7c6bf151 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -49,9 +49,6 @@ type pkgReader struct {
// but bitwise inverted so we can detect if we're missing the entry
// or not.
newindex []index
-
- // indicates whether the data is reading during reshaping.
- reshaping bool
}
func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
@@ -119,10 +116,6 @@ type reader struct {
// find parameters/results.
funarghack bool
- // reshaping is used during reading exprReshape code, preventing
- // the reader from shapifying the re-shaped type.
- reshaping bool
-
// methodSym is the name of method's name, if reading a method.
// It's nil if reading a normal function or closure body.
methodSym *types.Sym
@@ -937,8 +930,19 @@ func shapify(targ *types.Type, basic bool) *types.Type {
// types, and discarding struct field names and tags. However, we'll
// need to start tracking how type parameters are actually used to
// implement some of these optimizations.
+ pointerShaping := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
+ // The exception is when the type parameter is a pointer to a type
+ // which `Type.HasShape()` returns true, but `Type.IsShape()` returns
+ // false, like `*[]go.shape.T`. This is because the type parameter is
+ // used to instantiate a generic function inside another generic function.
+ // In this case, we want to keep the targ as-is, otherwise, we may lose the
+ // original type after `*[]go.shape.T` is shapified to `*go.shape.uint8`.
+ // See issue #54535, #71184.
+ if pointerShaping && !targ.Elem().IsShape() && targ.Elem().HasShape() {
+ return targ
+ }
under := targ.Underlying()
- if basic && targ.IsPtr() && !targ.Elem().NotInHeap() {
+ if pointerShaping {
under = types.NewPtr(types.Types[types.TUINT8])
}
@@ -1014,25 +1018,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
// arguments.
for i, targ := range dict.targs {
basic := r.Bool()
- isPointerShape := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
- // We should not do shapify during the reshaping process, see #71184.
- // However, this only matters for shapify a pointer type, which will
- // lose the original underlying type.
- //
- // Example with a pointer type:
- //
- // - First, shapifying *[]T -> *uint8
- // - During the reshaping process, *uint8 is shapified to *go.shape.uint8
- // - This ends up with a different type with the original *[]T
- //
- // For a non-pointer type:
- //
- // - int -> go.shape.int
- // - go.shape.int -> go.shape.int
- //
- // We always end up with the identical type.
- canShapify := !pr.reshaping || !isPointerShape
- if dict.shaped && canShapify {
+ if dict.shaped {
dict.targs[i] = shapify(targ, basic)
}
}
@@ -2445,8 +2431,16 @@ func (r *reader) expr() (res ir.Node) {
case exprNew:
pos := r.pos()
- typ := r.exprType()
- return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
+ if r.Bool() {
+ // new(expr) -> tmp := expr; &tmp
+ x := r.expr()
+ var init ir.Nodes
+ addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
+ addr.SetInit(init)
+ return typecheck.Expr(addr)
+ }
+ // new(T)
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, r.exprType()))
case exprSizeof:
return ir.NewUintptr(r.pos(), r.typ().Size())
@@ -2470,10 +2464,7 @@ func (r *reader) expr() (res ir.Node) {
case exprReshape:
typ := r.typ()
- old := r.reshaping
- r.reshaping = true
x := r.expr()
- r.reshaping = old
if types.IdenticalStrict(x.Type(), typ) {
return x
@@ -2596,10 +2587,7 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
info := r.dict.subdicts[idx]
explicits := r.p.typListIdx(info.explicits, r.dict)
- old := r.p.reshaping
- r.p.reshaping = r.reshaping
baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
- r.p.reshaping = old
// TODO(mdempsky): Is there a more robust way to get the
// dictionary pointer type here?
@@ -3259,6 +3247,7 @@ func (r *reader) exprType() ir.Node {
var rtype, itab ir.Node
if r.Bool() {
+ // non-empty interface
typ, rtype, _, _, itab = r.itab(pos)
if !typ.IsInterface() {
rtype = nil // TODO(mdempsky): Leave set?
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index 54e5f1ea5f..9c90d221c2 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -2035,10 +2035,16 @@ func (w *writer) expr(expr syntax.Expr) {
case "new":
assert(len(expr.ArgList) == 1)
assert(!expr.HasDots)
+ arg := expr.ArgList[0]
w.Code(exprNew)
w.pos(expr)
- w.exprType(nil, expr.ArgList[0])
+ tv := w.p.typeAndValue(arg)
+ if w.Bool(!tv.IsType()) {
+ w.expr(arg) // new(expr), go1.26
+ } else {
+ w.exprType(nil, arg) // new(T)
+ }
return
case "Sizeof":
diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go
index 48efdb5609..b1f787e03b 100644
--- a/src/cmd/compile/internal/ssa/tighten.go
+++ b/src/cmd/compile/internal/ssa/tighten.go
@@ -123,18 +123,21 @@ func tighten(f *Func) {
// If the target location is inside a loop,
// move the target location up to just before the loop head.
- for _, b := range f.Blocks {
- origloop := loops.b2l[b.ID]
- for _, v := range b.Values {
- t := target[v.ID]
- if t == nil {
- continue
- }
- targetloop := loops.b2l[t.ID]
- for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
- t = idom[targetloop.header.ID]
- target[v.ID] = t
- targetloop = loops.b2l[t.ID]
+ if !loops.hasIrreducible {
+ // Loop info might not be correct for irreducible loops. See issue 75569.
+ for _, b := range f.Blocks {
+ origloop := loops.b2l[b.ID]
+ for _, v := range b.Values {
+ t := target[v.ID]
+ if t == nil {
+ continue
+ }
+ targetloop := loops.b2l[t.ID]
+ for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
+ t = idom[targetloop.header.ID]
+ target[v.ID] = t
+ targetloop = loops.b2l[t.ID]
+ }
}
}
}
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index 4bb2135755..df207a2746 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -98,17 +98,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
y := args[1]
hasString := false
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
hasString = true
- return true
+ } else {
+ y = nil
+ break
}
- y = nil
- return false
- })
+ }
if y != nil && hasString {
// setting the signature also signals that we're done
sig = makeSig(x.typ, x.typ, y.typ)
@@ -368,16 +368,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
var special bool
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
special = true
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
- return true
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
+ } else {
+ special = false
+ break
}
- special = false
- return false
- })
+ }
}
// general case
@@ -636,11 +636,30 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
case _New:
- // new(T)
+ // new(T) or new(expr)
// (no argument evaluated yet)
- T := check.varType(argList[0])
- if !isValid(T) {
- return
+ arg := argList[0]
+ check.exprOrType(x, arg, true)
+ var T Type
+ switch x.mode {
+ case builtin:
+ check.errorf(x, UncalledBuiltin, "%s must be called", x)
+ x.mode = invalid
+ case typexpr:
+ // new(T)
+ T = x.typ
+ if !isValid(T) {
+ return
+ }
+ default:
+ // new(expr)
+ check.verifyVersionf(call.Fun, go1_26, "new(expr)")
+ T = Default(x.typ)
+ if T != x.typ {
+ // untyped constant: check for overflow.
+ check.assignment(x, T, "argument to new")
+ }
+ check.validVarType(arg, T)
}
x.mode = value
@@ -961,29 +980,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// or a type error if x is not a slice (or a type set of slices).
func sliceElem(x *operand) (Type, *typeError) {
var E Type
- var err *typeError
- typeset(x.typ, func(_, u Type) bool {
+ for _, u := range typeset(x.typ) {
s, _ := u.(*Slice)
if s == nil {
if x.isNil() {
// Printing x in this case would just print "nil".
// Special case this so we can emphasize "untyped".
- err = typeErrorf("argument must be a slice; have untyped nil")
+ return nil, typeErrorf("argument must be a slice; have untyped nil")
} else {
- err = typeErrorf("argument must be a slice; have %s", x)
+ return nil, typeErrorf("argument must be a slice; have %s", x)
}
- return false
}
if E == nil {
E = s.elem
} else if !Identical(E, s.elem) {
- err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
- return false
+ return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
}
- return true
- })
- if err != nil {
- return nil, err
}
return E, nil
}
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
index 80e8514168..7e16a87332 100644
--- a/src/cmd/compile/internal/types2/index.go
+++ b/src/cmd/compile/internal/types2/index.go
@@ -216,11 +216,11 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
// determine common underlying type cu
var ct, cu Type // type and respective common underlying type
var hasString bool
- typeset(x.typ, func(t, u Type) bool {
+ for t, u := range typeset(x.typ) {
if u == nil {
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
cu = nil
- return false
+ break
}
// Treat strings like byte slices but remember that we saw a string.
@@ -232,18 +232,16 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
cu = nil
- return false
+ break
}
-
- return true
- })
+ }
if hasString {
// If we saw a string, proceed with string type,
// but don't go from untyped string to string.
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index eaecb77af5..ea1cfd88cc 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -49,7 +49,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
}
last := params.At(n - 1).typ
var S *Slice
- typeset(last, func(t, _ Type) bool {
+ for t := range typeset(last) {
var s *Slice
if isString(t) {
s = NewSlice(universeByte)
@@ -60,10 +60,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
S = s
} else if !Identical(S, s) {
S = nil
- return false
+ break
}
- return true
- })
+ }
if S == nil {
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go
index fc67397503..26d2eb23ab 100644
--- a/src/cmd/compile/internal/types2/stdlib_test.go
+++ b/src/cmd/compile/internal/types2/stdlib_test.go
@@ -360,6 +360,7 @@ func TestStdKen(t *testing.T) {
var excluded = map[string]bool{
"builtin": true,
"cmd/compile/internal/ssa/_gen": true,
+ "runtime/_mkmalloc": true,
"simd/_gen/simdgen": true,
"simd/_gen/unify": true,
}
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
index a04f928908..c60b5eb417 100644
--- a/src/cmd/compile/internal/types2/typeparam.go
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -155,10 +155,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
return t.iface().typeSet().is(f)
}
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
// specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
- t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+ return t.iface().typeSet().all(f)
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 74436952f2..ce487e74f7 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -104,13 +104,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
// subsetOf reports whether s1 ⊆ s2.
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
if !s.hasTerms() {
- yield(nil, nil)
- return
+ return f(nil, nil)
}
for _, t := range s.terms {
@@ -123,10 +122,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
if debug {
assert(Identical(u, under(u)))
}
- if !yield(t.typ, u) {
- break
+ if !f(t.typ, u) {
+ return false
}
}
+ return true
}
// is calls f with the specific type terms of s and reports whether
diff --git a/src/cmd/compile/internal/types2/under.go b/src/cmd/compile/internal/types2/under.go
index 9e5334b724..078ba9ab17 100644
--- a/src/cmd/compile/internal/types2/under.go
+++ b/src/cmd/compile/internal/types2/under.go
@@ -4,6 +4,8 @@
package types2
+import "iter"
+
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
@@ -18,12 +20,18 @@ func under(t Type) Type {
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
// Otherwise, underIs returns the result of f(under(typ)).
func underIs(typ Type, f func(Type) bool) bool {
- var ok bool
- typeset(typ, func(_, u Type) bool {
- ok = f(u)
- return ok
+ return all(typ, func(_, u Type) bool {
+ return f(u)
})
- return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+ if p, _ := Unalias(t).(*TypeParam); p != nil {
+ return p.typeset(f)
+ }
+ return f(t, under(t))
}
// typeset is an iterator over the (type/underlying type) pairs of the
@@ -32,12 +40,10 @@ func underIs(typ Type, f func(Type) bool) bool {
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
- if p, _ := Unalias(t).(*TypeParam); p != nil {
- p.typeset(yield)
- return
+func typeset(t Type) iter.Seq2[Type, Type] {
+ return func(yield func(t, u Type) bool) {
+ _ = all(t, yield)
}
- yield(t, under(t))
}
// A typeError describes a type error.
@@ -80,35 +86,28 @@ func (err *typeError) format(check *Checker) string {
// with the single type t in its type set.
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
var ct, cu Type // type and respective common underlying type
- var err *typeError
-
- bad := func(format string, args ...any) bool {
- err = typeErrorf(format, args...)
- return false
- }
-
- typeset(t, func(t, u Type) bool {
+ for t, u := range typeset(t) {
if cond != nil {
- if err = cond(t, u); err != nil {
- return false
+ if err := cond(t, u); err != nil {
+ return nil, err
}
}
if u == nil {
- return bad("no specific type")
+ return nil, typeErrorf("no specific type")
}
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// If we've seen a channel before, and we have a channel now, they must be compatible.
if chu, _ := cu.(*Chan); chu != nil {
if ch, _ := u.(*Chan); ch != nil {
if !Identical(chu.elem, ch.elem) {
- return bad("channels %s and %s have different element types", ct, t)
+ return nil, typeErrorf("channels %s and %s have different element types", ct, t)
}
// If we have different channel directions, keep the restricted one
// and complain if they conflict.
@@ -118,22 +117,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
case chu.dir == SendRecv:
ct, cu = t, u // switch to restricted channel
case ch.dir != SendRecv:
- return bad("channels %s and %s have conflicting directions", ct, t)
+ return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
}
- return true
+ continue
}
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
- return bad("%s and %s have different underlying types", ct, t)
+ return nil, typeErrorf("%s and %s have different underlying types", ct, t)
}
-
- return true
- })
-
- if err != nil {
- return nil, err
}
return cu, nil
}
diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go
index b555f398da..765b0f7e9a 100644
--- a/src/cmd/compile/internal/types2/version.go
+++ b/src/cmd/compile/internal/types2/version.go
@@ -43,6 +43,7 @@ var (
go1_21 = asGoVersion("go1.21")
go1_22 = asGoVersion("go1.22")
go1_23 = asGoVersion("go1.23")
+ go1_26 = asGoVersion("go1.26")
// current (deployed) Go version
go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
diff --git a/src/cmd/compile/testdata/script/issue75461.txt b/src/cmd/compile/testdata/script/issue75461.txt
new file mode 100644
index 0000000000..05f0fd4cfa
--- /dev/null
+++ b/src/cmd/compile/testdata/script/issue75461.txt
@@ -0,0 +1,78 @@
+go build main.go
+! stdout .
+! stderr .
+
+-- main.go --
+package main
+
+import (
+ "demo/registry"
+)
+
+func main() {
+ _ = registry.NewUserRegistry()
+}
+
+-- go.mod --
+module demo
+
+go 1.24
+
+-- model/user.go --
+package model
+
+type User struct {
+ ID int
+}
+
+func (c *User) String() string {
+ return ""
+}
+
+-- ordered/map.go --
+package ordered
+
+type OrderedMap[K comparable, V any] struct {
+ m map[K]V
+}
+
+func New[K comparable, V any](options ...any) *OrderedMap[K, V] {
+ orderedMap := &OrderedMap[K, V]{}
+ return orderedMap
+}
+
+-- registry/user.go --
+package registry
+
+import (
+ "demo/model"
+ "demo/ordered"
+)
+
+type baseRegistry = Registry[model.User, *model.User]
+
+type UserRegistry struct {
+ *baseRegistry
+}
+
+type Registry[T any, P PStringer[T]] struct {
+ m *ordered.OrderedMap[string, P]
+}
+
+type PStringer[T any] interface {
+ *T
+ String() string
+}
+
+func NewRegistry[T any, P PStringer[T]]() *Registry[T, P] {
+ r := &Registry[T, P]{
+ m: ordered.New[string, P](),
+ }
+ return r
+}
+
+func NewUserRegistry() *UserRegistry {
+ return &UserRegistry{
+ baseRegistry: NewRegistry[model.User](),
+ }
+}