]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: only report version errors if new(expr) is ok otherwise
authorRobert Griesemer <gri@google.com>
Mon, 20 Oct 2025 22:02:23 +0000 (15:02 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 21 Oct 2025 21:41:38 +0000 (14:41 -0700)
If new(expr) is used before Go 1.26, don't report version errors if there
are other problems with the expression.

While at it, implement multiple missing type checks for new(expr) and
add corresponding test cases that were missed in CL 704935 (tests for
no value expressions, generic types, untyped nil).

Reorganize/rename builtins0.go tests for new to match existing test case
patterns again.

Fixes #75986.
For #45624.

Change-Id: I39e5516d3f8d191cc390a4d8b9911c312bbb177c
Reviewed-on: https://go-review.googlesource.com/c/go/+/713241
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/builtins.go
src/go/types/builtins.go
src/internal/types/testdata/check/builtins0.go
src/internal/types/testdata/check/go1_25.go
src/internal/types/testdata/fixedbugs/issue75986.go [new file with mode: 0644]

index 84acb4ca484164f8ea53ef2986d1c6ee69f6b422..d4463bc4b09a5ff727134aa4846f9568a8dec0f9 100644 (file)
@@ -639,31 +639,31 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                // new(T) or new(expr)
                // (no argument evaluated yet)
                arg := argList[0]
-               check.exprOrType(x, arg, true)
-               var T Type
+               check.exprOrType(x, arg, false)
+               check.exclude(x, 1<<novalue|1<<builtin)
                switch x.mode {
-               case builtin:
-                       check.errorf(x, UncalledBuiltin, "%s must be called", x)
-                       x.mode = invalid
+               case invalid:
+                       return
                case typexpr:
                        // new(T)
-                       T = x.typ
-                       if !isValid(T) {
-                               return
-                       }
+                       check.validVarType(arg, x.typ)
                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")
+                       if isUntyped(x.typ) {
+                               // check for overflow and untyped nil
+                               check.assignment(x, nil, "argument to new")
+                               if x.mode == invalid {
+                                       return
+                               }
+                               assert(isTyped(x.typ))
                        }
-                       check.validVarType(arg, T)
+                       // report version error only if there are no other errors
+                       check.verifyVersionf(call.Fun, go1_26, "new(%s)", arg)
                }
 
+               T := x.typ
                x.mode = value
-               x.typ = &Pointer{base: T}
+               x.typ = NewPointer(T)
                if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
                }
index f59c9ddd1203603fcefaa11dc7bab5727e4966a5..04d294db0c11e6eb6c91195e0d5fae8da143fa03 100644 (file)
@@ -642,31 +642,31 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                // new(T) or new(expr)
                // (no argument evaluated yet)
                arg := argList[0]
-               check.exprOrType(x, arg, true)
-               var T Type
+               check.exprOrType(x, arg, false)
+               check.exclude(x, 1<<novalue|1<<builtin)
                switch x.mode {
-               case builtin:
-                       check.errorf(x, UncalledBuiltin, "%s must be called", x)
-                       x.mode = invalid
+               case invalid:
+                       return
                case typexpr:
                        // new(T)
-                       T = x.typ
-                       if !isValid(T) {
-                               return
-                       }
+                       check.validVarType(arg, x.typ)
                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")
+                       if isUntyped(x.typ) {
+                               // check for overflow and untyped nil
+                               check.assignment(x, nil, "argument to new")
+                               if x.mode == invalid {
+                                       return
+                               }
+                               assert(isTyped(x.typ))
                        }
-                       check.validVarType(arg, T)
+                       // report version error only if there are no other errors
+                       check.verifyVersionf(call.Fun, go1_26, "new(%s)", arg)
                }
 
+               T := x.typ
                x.mode = value
-               x.typ = &Pointer{base: T}
+               x.typ = NewPointer(T)
                if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
                }
index e326b92ac7bbd104c3820d62bd9365cfe1c27e59..9b99a890acf996376e063288d6d27edebeefe995 100644 (file)
@@ -609,36 +609,38 @@ func min2() {
        )
 }
 
-func newInvalid() {
-       f2 := func() (x, y int) { return }
 
+func new1() {
        _ = new()     // ERROR "not enough arguments"
        _ = new(1, 2) // ERROR "too many arguments"
+       _ = new(unsafe /* ERROR "use of package unsafe not in selector" */ )
+
+       _ = new(struct{ x, y int })
+       p := new(float64)
+       q := new(*float64)
+       _ = *p == **q
+
+       type G[P any] struct{}
+       _ = new(G[int])
+       _ = new(G /* ERROR "cannot use generic type G without instantiation" */ )
+
        new /* ERROR "not used" */ (int)
        _ = &new /* ERROR "cannot take address" */ (int)
        _ = new(int... /* ERROR "invalid use of ..." */)
        _ = new(f0 /* ERROR "f0() (no value) used as value or type" */ ())
        _ = new(len /* ERROR "len (built-in) must be called" */)
        _ = new(1 /* ERROR "argument to new (overflows)" */ << 70)
-       _ = new(f2 /* ERRORx "multiple-value.*in single-value context" */ ())
 }
 
-// new(T)
-func newType() {
-       _ = new(struct{ x, y int })
-
-       p := new(float64)
-       q := new(*float64)
-       _ = *p == **q
-}
-
-// new(expr), added in go1.26
-func newExpr() {
-       f1 := func() (x []int) { return }
+func new2() {
+       // new(expr), added in go1.26
+       f1 := func() []int { panic(0) }
+       f2 := func() (int, int) { panic(0) }
        var (
                _ *[]int        = new(f1())
                _ *func() []int = new(f1)
                _ *bool         = new(false)
+               _ *bool         = new(1 < 2)
                _ *int          = new(123)
                _ *float64      = new(1.0)
                _ *uint         = new(uint(3))
@@ -647,6 +649,14 @@ func newExpr() {
                _ *struct{}     = new(struct{}{})
                _ *any          = new(any)
 
+               _ = new(f2 /* ERRORx "multiple-value.*in single-value context" */ ())
+               _ = new(1 << /* ERROR "constant shift overflow" */ 1000)
+               _ = new(1e10000 /* ERROR "cannot use 1e10000 (untyped float constant 1e+10000) as float64 value in argument to new (overflows)" */ )
+               _ = new(nil /* ERROR "use of untyped nil in argument to new" */ )
+               _ = new(comparable /* ERROR "cannot use type comparable outside a type constraint" */ )
+               _ = new(new /* ERROR "new (built-in) must be called" */ )
+               _ = new(panic /* ERROR "panic(0) (no value) used as value or type" */ (0))
+
                // from issue 43125
                _ = new(-1)
                _ = new(1 + 1)
index b2ace833439661eeaad2a0a2e4efaa79924d9700..3799bc02b46466a1ffd6dc162a8db4a329a83d5c 100644 (file)
@@ -10,4 +10,9 @@
 
 package p
 
-var _ = new /* ERROR "new(expr) requires go1.26 or later" */ (123)
+func f(x int) {
+       _ = new /* ERROR "new(123) requires go1.26 or later" */ (123)
+       _ = new /* ERROR "new(x) requires go1.26 or later" */ (x)
+       _ = new /* ERROR "new(f) requires go1.26 or later" */ (f)
+       _ = new /* ERROR "new(1 < 2) requires go1.26 or later" */ (1 < 2)
+}
diff --git a/src/internal/types/testdata/fixedbugs/issue75986.go b/src/internal/types/testdata/fixedbugs/issue75986.go
new file mode 100644 (file)
index 0000000..b2b1509
--- /dev/null
@@ -0,0 +1,28 @@
+// -lang=go1.25
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "strings"
+
+type T int
+type G[P any] struct{}
+
+var x T
+
+// Verify that we don't get a version error when there's another error present in new(expr).
+
+func f() {
+       _ = new(U /* ERROR "undefined: U" */)
+       _ = new(strings.BUILDER /* ERROR "undefined: strings.BUILDER (but have Builder)" */)
+       _ = new(T)      // ok
+       _ = new(G[int]) // ok
+       _ = new(G /* ERROR "cannot use generic type G without instantiation" */)
+       _ = new(nil /* ERROR "use of untyped nil in argument to new" */)
+       _ = new(comparable /* ERROR "cannot use type comparable outside a type constraint" */)
+       _ = new(new /* ERROR "new (built-in) must be called" */)
+       _ = new(panic /* ERROR "panic(0) (no value) used as value or type" */ (0))
+}